From 0e5a38cde2ede896e4dca6d85f133d9e23ec72cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Esser?= Date: Wed, 1 Aug 2018 11:51:23 +0200 Subject: [PATCH 01/10] Bump tarball version to 4.1.2 and update NEWS. --- NEWS | 2 ++ configure.ac | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 0288c44..40effdc 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,8 @@ libxcrypt NEWS -- history of user-visible changes. Please send bug reports, questions and suggestions to . +Version 4.1.2 + Version 4.1.1 * --enable-hashes now supports additional groups of hashing methods: 'freebsd', 'netbsd', 'openbsd', 'osx', and 'solaris', which select diff --git a/configure.ac b/configure.ac index a22a592..8940e2d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # Process this file with autoconf to produce a configure script. m4_include([m4/zw_automodern.m4]) AC_INIT([xcrypt], - [4.1.1], + [4.1.2], [https://github.com/besser82/libxcrypt/issues], [libxcrypt], [https://github.com/besser82/libxcrypt]) From 7281ceb60e404e8734784670490aaf7d95ffb970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Esser?= Date: Thu, 2 Aug 2018 09:44:07 +0200 Subject: [PATCH 02/10] Use valgrind memcheck if available to test for memory leaks. --- .travis.yml | 5 +- Makefile.am | 11 +- NEWS | 1 + configure.ac | 13 +- m4/ax_valgrind_check.m4 | 239 ++++++++++++++++++ test-symbols-compat => test-symbols-compat.sh | 0 ...symbols-renames => test-symbols-renames.sh | 0 test-symbols-static => test-symbols-static.sh | 0 8 files changed, 261 insertions(+), 8 deletions(-) create mode 100644 m4/ax_valgrind_check.m4 rename test-symbols-compat => test-symbols-compat.sh (100%) rename test-symbols-renames => test-symbols-renames.sh (100%) rename test-symbols-static => test-symbols-static.sh (100%) diff --git a/.travis.yml b/.travis.yml index db45ded..1f8e224 100644 --- a/.travis.yml +++ b/.travis.yml @@ -155,7 +155,7 @@ before_install: - docker exec -t buildenv /bin/sh -c 'for i in `seq 0 99`; do dnf makecache && i= && break || sleep 1; done; [ -z "$i" ]' - docker exec -t buildenv /bin/sh -c 'for i in `seq 0 99`; do dnf -y upgrade && i= && break || sleep 1; done; [ -z "$i" ]' - docker exec -t buildenv /bin/sh -c 'for i in `seq 0 99`; do dnf -y groups install buildsys-build && i= && break || sleep 1; done; [ -z "$i" ]' - - docker exec -t buildenv /bin/sh -c 'for i in `seq 0 99`; do dnf -y install libtool && i= && break || sleep 1; done; [ -z "$i" ]' + - docker exec -t buildenv /bin/sh -c 'for i in `seq 0 99`; do dnf -y install libtool valgrind && i= && break || sleep 1; done; [ -z "$i" ]' - if [[ "$CC" == "clang" ]] ; then docker exec -t buildenv /bin/sh -c 'for i in `seq 0 99`; do dnf -y install clang && i= && break || sleep 1; done; [ -z "$i" ]' ; fi - if [[ "$CODECOV" == "1" ]] ; then docker exec -t buildenv /bin/sh -c 'for i in `seq 0 99`; do dnf -y install '/usr/bin/git' '/usr/bin/lcov' '/usr/bin/pip3' && i= && break || sleep 1; done; [ -z "$i" ]' ; fi - if [[ "$CODECOV" == "1" ]] ; then docker exec -t buildenv /bin/sh -c "pip3 install codecov" ; fi @@ -178,6 +178,7 @@ script: - docker exec -t buildenv /bin/sh -c "make -C /opt/libxcrypt -j$((`nproc --all` * 2))" - docker exec -t buildenv /bin/sh -c "make -C /opt/libxcrypt install" - docker exec -t buildenv /bin/sh -c "(make -C /opt/libxcrypt -j$((`nproc --all` * 2)) check || (cat /opt/libxcrypt/test-suite.log && exit 1))" + - docker exec -t buildenv /bin/sh -c "(make -C /opt/libxcrypt -j$((`nproc --all` * 2)) check-valgrind-memcheck || (cat /opt/libxcrypt/test-suite-memcheck.log && exit 1))" after_success: - - if [[ "$CODECOV" == "1" ]] ; then docker exec -t buildenv /bin/sh -c "make -C /opt/libxcrypt check && cd /opt/libxcrypt && lcov --directory . --capture --output-file all_coverage.info && lcov --remove all_coverage.info '/usr/*' '*test*' > coverage.info && rm all_coverage.info && codecov -X gcov" ; fi + - if [[ "$CODECOV" == "1" ]] ; then docker exec -t buildenv /bin/sh -c "cd /opt/libxcrypt && lcov --directory . --capture --output-file all_coverage.info && lcov --remove all_coverage.info '/usr/*' '*test*' > coverage.info && rm all_coverage.info && codecov -X gcov" ; fi diff --git a/Makefile.am b/Makefile.am index 201dea5..874db12 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,6 +9,9 @@ ACLOCAL_AMFLAGS = -I m4 AM_CFLAGS = $(WARN_CFLAGS) +@VALGRIND_CHECK_RULES@ +TEST_EXTENSIONS = .sh + EXTRA_DIST = \ LICENSING THANKS \ libcrypt.map.in libcrypt.minver \ @@ -158,15 +161,15 @@ endif TESTS = $(check_PROGRAMS) if ENABLE_STATIC -TESTS += test-symbols-static +TESTS += test-symbols-static.sh if HAVE_CPP_dD -TESTS += test-symbols-renames +TESTS += test-symbols-renames.sh endif endif if ENABLE_OBSOLETE_API -TESTS += test-symbols-compat +TESTS += test-symbols-compat.sh endif -EXTRA_DIST += test-symbols-static test-symbols-compat test-symbols-renames +EXTRA_DIST += test-symbols-static.sh test-symbols-compat.sh test-symbols-renames.sh AM_TESTS_ENVIRONMENT = \ lib_la="./libcrypt.la"; lib_map="$(srcdir)/libcrypt.map.in"; \ diff --git a/NEWS b/NEWS index 40effdc..77b556f 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ Please send bug reports, questions and suggestions to . Version 4.1.2 +* Add optional 'check-valgrind' target to the Makefile. Version 4.1.1 * --enable-hashes now supports additional groups of hashing methods: diff --git a/configure.ac b/configure.ac index 8940e2d..9ea30f6 100644 --- a/configure.ac +++ b/configure.ac @@ -51,7 +51,6 @@ AC_C_BIGENDIAN( AC_DEFINE([IS_BIGENDIAN], 0, [Define to 0 if system's architecture is little-endian.]) ) - # One of the test scripts needs to use -dD. AC_CACHE_CHECK([whether the preprocessor ($CPP) supports -dD], [ac_cv_prog_cc_dD], @@ -151,8 +150,18 @@ AC_CHECK_FUNCS_ONCE([ syscall ]) -# Configure options. +# Disable valgrind tools for checking multithreaded +# programs, as we don't use them in checks. +AX_VALGRIND_DFLT([drd], [off]) +AX_VALGRIND_DFLT([helgrind], [off]) + +# Valgrind's sgcheck is b0rk3n upstream. +AX_VALGRIND_DFLT([sgcheck], [off]) +# Add a target to run testsuite with valgrind. +AX_VALGRIND_CHECK() + +# Configure options. AC_ARG_ENABLE([obsolete-api], AS_HELP_STRING( [--enable-obsolete-api[=ARG]], diff --git a/m4/ax_valgrind_check.m4 b/m4/ax_valgrind_check.m4 new file mode 100644 index 0000000..7033798 --- /dev/null +++ b/m4/ax_valgrind_check.m4 @@ -0,0 +1,239 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_VALGRIND_DFLT(memcheck|helgrind|drd|sgcheck, on|off) +# AX_VALGRIND_CHECK() +# +# DESCRIPTION +# +# AX_VALGRIND_CHECK checks whether Valgrind is present and, if so, allows +# running `make check` under a variety of Valgrind tools to check for +# memory and threading errors. +# +# Defines VALGRIND_CHECK_RULES which should be substituted in your +# Makefile; and $enable_valgrind which can be used in subsequent configure +# output. VALGRIND_ENABLED is defined and substituted, and corresponds to +# the value of the --enable-valgrind option, which defaults to being +# enabled if Valgrind is installed and disabled otherwise. Individual +# Valgrind tools can be disabled via --disable-valgrind-, the +# default is configurable via the AX_VALGRIND_DFLT command or is to use +# all commands not disabled via AX_VALGRIND_DFLT. All AX_VALGRIND_DFLT +# calls must be made before the call to AX_VALGRIND_CHECK. +# +# If unit tests are written using a shell script and automake's +# LOG_COMPILER system, the $(VALGRIND) variable can be used within the +# shell scripts to enable Valgrind, as described here: +# +# https://www.gnu.org/software/gnulib/manual/html_node/Running-self_002dtests-under-valgrind.html +# +# Usage example: +# +# configure.ac: +# +# AX_VALGRIND_DFLT([sgcheck], [off]) +# AX_VALGRIND_CHECK +# +# in each Makefile.am with tests: +# +# @VALGRIND_CHECK_RULES@ +# VALGRIND_SUPPRESSIONS_FILES = my-project.supp +# EXTRA_DIST = my-project.supp +# +# This results in a "check-valgrind" rule being added. Running `make +# check-valgrind` in that directory will recursively run the module's test +# suite (`make check`) once for each of the available Valgrind tools (out +# of memcheck, helgrind and drd) while the sgcheck will be skipped unless +# enabled again on the commandline with --enable-valgrind-sgcheck. The +# results for each check will be output to test-suite-$toolname.log. The +# target will succeed if there are zero errors and fail otherwise. +# +# Alternatively, a "check-valgrind-$TOOL" rule will be added, for $TOOL in +# memcheck, helgrind, drd and sgcheck. These are useful because often only +# some of those tools can be ran cleanly on a codebase. +# +# The macro supports running with and without libtool. +# +# LICENSE +# +# Copyright (c) 2014, 2015, 2016 Philip Withnall +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 17 + +dnl Configured tools +m4_define([valgrind_tool_list], [[memcheck], [helgrind], [drd], [sgcheck]]) +m4_set_add_all([valgrind_exp_tool_set], [sgcheck]) +m4_foreach([vgtool], [valgrind_tool_list], + [m4_define([en_dflt_valgrind_]vgtool, [on])]) + +AC_DEFUN([AX_VALGRIND_DFLT],[ + m4_define([en_dflt_valgrind_$1], [$2]) +])dnl + +AM_EXTRA_RECURSIVE_TARGETS([check-valgrind]) +m4_foreach([vgtool], [valgrind_tool_list], + [AM_EXTRA_RECURSIVE_TARGETS([check-valgrind-]vgtool)]) + +AC_DEFUN([AX_VALGRIND_CHECK],[ + dnl Check for --enable-valgrind + AC_ARG_ENABLE([valgrind], + [AS_HELP_STRING([--enable-valgrind], [Whether to enable Valgrind on the unit tests])], + [enable_valgrind=$enableval],[enable_valgrind=]) + + AS_IF([test "$enable_valgrind" != "no"],[ + # Check for Valgrind. + AC_CHECK_PROG([VALGRIND],[valgrind],[valgrind]) + AS_IF([test "$VALGRIND" = ""],[ + AS_IF([test "$enable_valgrind" = "yes"],[ + AC_MSG_ERROR([Could not find valgrind; either install it or reconfigure with --disable-valgrind]) + ],[ + enable_valgrind=no + ]) + ],[ + enable_valgrind=yes + ]) + ]) + + AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"]) + AC_SUBST([VALGRIND_ENABLED],[$enable_valgrind]) + + # Check for Valgrind tools we care about. + [valgrind_enabled_tools=] + m4_foreach([vgtool],[valgrind_tool_list],[ + AC_ARG_ENABLE([valgrind-]vgtool, + m4_if(m4_defn([en_dflt_valgrind_]vgtool),[off],dnl +[AS_HELP_STRING([--enable-valgrind-]vgtool, [Whether to use ]vgtool[ during the Valgrind tests])],dnl +[AS_HELP_STRING([--disable-valgrind-]vgtool, [Whether to skip ]vgtool[ during the Valgrind tests])]), + [enable_valgrind_]vgtool[=$enableval], + [enable_valgrind_]vgtool[=]) + AS_IF([test "$enable_valgrind" = "no"],[ + enable_valgrind_]vgtool[=no], + [test "$enable_valgrind_]vgtool[" ]dnl +m4_if(m4_defn([en_dflt_valgrind_]vgtool), [off], [= "yes"], [!= "no"]),[ + AC_CACHE_CHECK([for Valgrind tool ]vgtool, + [ax_cv_valgrind_tool_]vgtool,[ + ax_cv_valgrind_tool_]vgtool[=no + m4_set_contains([valgrind_exp_tool_set],vgtool, + [m4_define([vgtoolx],[exp-]vgtool)], + [m4_define([vgtoolx],vgtool)]) + AS_IF([`$VALGRIND --tool=]vgtoolx[ --help >/dev/null 2>&1`],[ + ax_cv_valgrind_tool_]vgtool[=yes + ]) + ]) + AS_IF([test "$ax_cv_valgrind_tool_]vgtool[" = "no"],[ + AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[ + AC_MSG_ERROR([Valgrind does not support ]vgtool[; reconfigure with --disable-valgrind-]vgtool) + ],[ + enable_valgrind_]vgtool[=no + ]) + ],[ + enable_valgrind_]vgtool[=yes + ]) + ]) + AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[ + valgrind_enabled_tools="$valgrind_enabled_tools ]m4_bpatsubst(vgtool,[^exp-])[" + ]) + AC_SUBST([ENABLE_VALGRIND_]vgtool,[$enable_valgrind_]vgtool) + ]) + AC_SUBST([valgrind_tools],["]m4_join([ ], valgrind_tool_list)["]) + AC_SUBST([valgrind_enabled_tools],[$valgrind_enabled_tools]) + +[VALGRIND_CHECK_RULES=' +# Valgrind check +# +# Optional: +# - VALGRIND_SUPPRESSIONS_FILES: Space-separated list of Valgrind suppressions +# files to load. (Default: empty) +# - VALGRIND_FLAGS: General flags to pass to all Valgrind tools. +# (Default: --num-callers=30) +# - VALGRIND_$toolname_FLAGS: Flags to pass to Valgrind $toolname (one of: +# memcheck, helgrind, drd, sgcheck). (Default: various) + +# Optional variables +VALGRIND_SUPPRESSIONS ?= $(addprefix --suppressions=,$(VALGRIND_SUPPRESSIONS_FILES)) +VALGRIND_FLAGS ?= --num-callers=30 +VALGRIND_memcheck_FLAGS ?= --leak-check=full --show-reachable=no +VALGRIND_helgrind_FLAGS ?= --history-level=approx +VALGRIND_drd_FLAGS ?= +VALGRIND_sgcheck_FLAGS ?= + +# Internal use +valgrind_log_files = $(addprefix test-suite-,$(addsuffix .log,$(valgrind_tools))) + +valgrind_memcheck_flags = --tool=memcheck $(VALGRIND_memcheck_FLAGS) +valgrind_helgrind_flags = --tool=helgrind $(VALGRIND_helgrind_FLAGS) +valgrind_drd_flags = --tool=drd $(VALGRIND_drd_FLAGS) +valgrind_sgcheck_flags = --tool=exp-sgcheck $(VALGRIND_sgcheck_FLAGS) + +valgrind_quiet = $(valgrind_quiet_$(V)) +valgrind_quiet_ = $(valgrind_quiet_$(AM_DEFAULT_VERBOSITY)) +valgrind_quiet_0 = --quiet +valgrind_v_use = $(valgrind_v_use_$(V)) +valgrind_v_use_ = $(valgrind_v_use_$(AM_DEFAULT_VERBOSITY)) +valgrind_v_use_0 = @echo " USE " $(patsubst check-valgrind-%-am,%,$''@):; + +# Support running with and without libtool. +ifneq ($(LIBTOOL),) +valgrind_lt = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=execute +else +valgrind_lt = +endif + +# Use recursive makes in order to ignore errors during check +check-valgrind-am: +ifeq ($(VALGRIND_ENABLED),yes) + $(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k \ + $(foreach tool, $(valgrind_enabled_tools), check-valgrind-$(tool)) +else + @echo "Need to reconfigure with --enable-valgrind" +endif + +# Valgrind running +VALGRIND_TESTS_ENVIRONMENT = \ + $(TESTS_ENVIRONMENT) \ + env VALGRIND=$(VALGRIND) \ + G_SLICE=always-malloc,debug-blocks \ + G_DEBUG=fatal-warnings,fatal-criticals,gc-friendly + +VALGRIND_LOG_COMPILER = \ + $(valgrind_lt) \ + $(VALGRIND) $(VALGRIND_SUPPRESSIONS) --error-exitcode=1 $(VALGRIND_FLAGS) + +define valgrind_tool_rule +check-valgrind-$(1)-am: +ifeq ($$(VALGRIND_ENABLED)-$$(ENABLE_VALGRIND_$(1)),yes-yes) +ifneq ($$(TESTS),) + $$(valgrind_v_use)$$(MAKE) check-TESTS \ + TESTS_ENVIRONMENT="$$(VALGRIND_TESTS_ENVIRONMENT)" \ + LOG_COMPILER="$$(VALGRIND_LOG_COMPILER)" \ + LOG_FLAGS="$$(valgrind_$(1)_flags)" \ + TEST_SUITE_LOG=test-suite-$(1).log +endif +else ifeq ($$(VALGRIND_ENABLED),yes) + @echo "Need to reconfigure with --enable-valgrind-$(1)" +else + @echo "Need to reconfigure with --enable-valgrind" +endif +endef + +$(foreach tool,$(valgrind_tools),$(eval $(call valgrind_tool_rule,$(tool)))) + +A''M_DISTCHECK_CONFIGURE_FLAGS ?= +A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-valgrind + +MOSTLYCLEANFILES ?= +MOSTLYCLEANFILES += $(valgrind_log_files) + +.PHONY: check-valgrind $(add-prefix check-valgrind-,$(valgrind_tools)) +'] + + AC_SUBST([VALGRIND_CHECK_RULES]) + m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([VALGRIND_CHECK_RULES])]) +]) diff --git a/test-symbols-compat b/test-symbols-compat.sh similarity index 100% rename from test-symbols-compat rename to test-symbols-compat.sh diff --git a/test-symbols-renames b/test-symbols-renames.sh similarity index 100% rename from test-symbols-renames rename to test-symbols-renames.sh diff --git a/test-symbols-static b/test-symbols-static.sh similarity index 100% rename from test-symbols-static rename to test-symbols-static.sh From 307c223a0f8c7b52678bf3fed59685932ec57c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Esser?= Date: Sat, 14 Jul 2018 20:36:30 +0200 Subject: [PATCH 03/10] Replace crypt-sha256.c with an implementation in the Public Domain. This implementation is based on the SHA256-based Unix crypt implementation. Released into the Public Domain [1] by Ulrich Drepper . This file is a modified except from [2], lines 648 up to 909. [1] https://www.akkadia.org/drepper/sha-crypt.html [2] https://www.akkadia.org/drepper/SHA-crypt.txt --- crypt-sha256.c | 351 +++++++++++++++++++++++++------------------------ 1 file changed, 176 insertions(+), 175 deletions(-) diff --git a/crypt-sha256.c b/crypt-sha256.c index e8d3808..c2954a4 100644 --- a/crypt-sha256.c +++ b/crypt-sha256.c @@ -1,20 +1,17 @@ -/* One way encryption based on SHA256 sum. - - Copyright (C) 2007-2017 Free Software Foundation, Inc. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 of - the License, or (at your option) any later version. - - This library 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, see - . */ +/* One way encryption based on the SHA256-based Unix crypt implementation. + * + * Written by Ulrich Drepper in 2007 [1]. + * To the extent possible under law, Ulrich Drepper has waived all + * copyright and related or neighboring rights to this work. + * + * See https://creativecommons.org/publicdomain/zero/1.0/ for further + * details. + * + * This file is an except from [2], lines 648 up to 909. + * + * [1] https://www.akkadia.org/drepper/sha-crypt.html + * [2] https://www.akkadia.org/drepper/SHA-crypt.txt + */ #include "crypt-port.h" #include "crypt-private.h" @@ -42,35 +39,8 @@ static const char sha256_rounds_prefix[] = "rounds="; /* Maximum number of rounds. */ #define ROUNDS_MAX 999999999 -/* The maximum possible length of a SHA256-hashed password string, - including the terminating NUL character. Prefix (including its NUL) - + rounds tag ("rounds=$" = "rounds=\0") + strlen(ROUNDS_MAX) - + salt (up to SALT_LEN_MAX chars) + '$' + hash (43 chars). */ - -#define LENGTH_OF_NUMBER(n) (sizeof #n - 1) - -#define SHA256_HASH_LENGTH \ - (sizeof (sha256_salt_prefix) + sizeof (sha256_rounds_prefix) + \ - LENGTH_OF_NUMBER (ROUNDS_MAX) + SALT_LEN_MAX + 1 + 43) - -static_assert (SHA256_HASH_LENGTH <= CRYPT_OUTPUT_SIZE, - "CRYPT_OUTPUT_SIZE is too small for SHA256"); - -/* A sha256_buffer holds all of the sensitive intermediate data. */ -struct sha256_buffer -{ - struct sha256_ctx ctx; - uint8_t result[32]; - uint8_t p_bytes[32]; - uint8_t s_bytes[32]; -}; - -static_assert (sizeof (struct sha256_buffer) <= ALG_SPECIFIC_SIZE, - "ALG_SPECIFIC_SIZE is too small for SHA256"); - - /* Table with characters for base64 transformation. */ -static const char b64t[] = +static const char b64t[64] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /* Subroutine of _xcrypt_crypt_sha256_rn: Feed CTX with LEN bytes of a @@ -86,30 +56,23 @@ sha256_process_recycled_bytes (unsigned char block[32], size_t len, sha256_process_bytes (block, cnt, ctx); } -void -crypt_sha256_rn (const char *phrase, size_t phr_size, - const char *setting, size_t ARG_UNUSED (set_size), - uint8_t *output, size_t out_size, - void *scratch, size_t scr_size) +static char * +sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen) { - /* This shouldn't ever happen, but... */ - if (out_size < SHA256_HASH_LENGTH - || scr_size < sizeof (struct sha256_buffer)) - { - errno = ERANGE; - return; - } - - struct sha256_buffer *buf = scratch; - struct sha256_ctx *ctx = &buf->ctx; - uint8_t *result = buf->result; - uint8_t *p_bytes = buf->p_bytes; - uint8_t *s_bytes = buf->s_bytes; - char *cp = (char *)output; - const char *salt = setting; - - size_t salt_size; + unsigned char alt_result[32] + __attribute__ ((__aligned__ (__alignof__ (uint32_t)))); + unsigned char temp_result[32] + __attribute__ ((__aligned__ (__alignof__ (uint32_t)))); + struct sha256_ctx ctx; + struct sha256_ctx alt_ctx; + size_t salt_len; + size_t key_len; size_t cnt; + char *cp; + char *copied_key = NULL; + char *copied_salt = NULL; + char *p_bytes; + char *s_bytes; /* Default number of rounds. */ size_t rounds = ROUNDS_DEFAULT; bool rounds_custom = false; @@ -124,176 +87,214 @@ crypt_sha256_rn (const char *phrase, size_t phr_size, == 0) { const char *num = salt + sizeof (sha256_rounds_prefix) - 1; - /* Do not allow an explicit setting of zero rounds, nor of the - default number of rounds, nor leading zeroes on the rounds. */ - if (!(*num >= '1' && *num <= '9')) - { - errno = EINVAL; - return; - } - - errno = 0; char *endp; - rounds = strtoul (num, &endp, 10); - if (endp == num || *endp != '$' - || rounds < ROUNDS_MIN - || rounds > ROUNDS_MAX - || errno) + unsigned long int srounds = strtoul (num, &endp, 10); + if (*endp == '$') { - errno = EINVAL; - return; + salt = endp + 1; + rounds = MAX (ROUNDS_MIN, MIN (srounds, ROUNDS_MAX)); + rounds_custom = true; } - salt = endp + 1; - rounds_custom = true; } - salt_size = strspn (salt, b64t); - if (salt[salt_size] && salt[salt_size] != '$') + salt_len = MIN (strcspn (salt, "$"), SALT_LEN_MAX); + key_len = strlen (key); + + if ((key - (char *) 0) % __alignof__ (uint32_t) != 0) { - errno = EINVAL; - return; + char *tmp = (char *) alloca (key_len + __alignof__ (uint32_t)); + key = copied_key = + memcpy (tmp + __alignof__ (uint32_t) + - (tmp - (char *) 0) % __alignof__ (uint32_t), + key, key_len); } - if (salt_size > SALT_LEN_MAX) - salt_size = SALT_LEN_MAX; - /* Compute alternate SHA256 sum with input PHRASE, SALT, and PHRASE. The - final result will be added to the first context. */ - sha256_init_ctx (ctx); + if ((salt - (char *) 0) % __alignof__ (uint32_t) != 0) + { + char *tmp = (char *) alloca (salt_len + __alignof__ (uint32_t)); + salt = copied_salt = + memcpy (tmp + __alignof__ (uint32_t) + - (tmp - (char *) 0) % __alignof__ (uint32_t), + salt, salt_len); + } - /* Add phrase. */ - sha256_process_bytes (phrase, phr_size, ctx); + /* Prepare for the real work. */ + sha256_init_ctx (&ctx); - /* Add salt. */ - sha256_process_bytes (salt, salt_size, ctx); + /* Add the key string. */ + sha256_process_bytes (key, key_len, &ctx); - /* Add phrase again. */ - sha256_process_bytes (phrase, phr_size, ctx); + /* The last part is the salt string. This must be at most 16 + characters and it ends at the first `$' character (for + compatibility with existing implementations). */ + sha256_process_bytes (salt, salt_len, &ctx); - /* Now get result of this (32 bytes). */ - sha256_finish_ctx (ctx, result); + /* Compute alternate SHA256 sum with input KEY, SALT, and KEY. The + final result will be added to the first context. */ + sha256_init_ctx (&alt_ctx); - /* Prepare for the real work. */ - sha256_init_ctx (ctx); + /* Add key. */ + sha256_process_bytes (key, key_len, &alt_ctx); - /* Add the phrase string. */ - sha256_process_bytes (phrase, phr_size, ctx); + /* Add salt. */ + sha256_process_bytes (salt, salt_len, &alt_ctx); - /* The last part is the salt string. This must be at most 8 - characters and it ends at the first `$' character (for - compatibility with existing implementations). */ - sha256_process_bytes (salt, salt_size, ctx); + /* Add key again. */ + sha256_process_bytes (key, key_len, &alt_ctx); + + /* Now get result of this (32 bytes) and add it to the other + context. */ + sha256_finish_ctx (&alt_ctx, alt_result); - /* Add for any character in the phrase one byte of the alternate sum. */ - for (cnt = phr_size; cnt > 32; cnt -= 32) - sha256_process_bytes (result, 32, ctx); - sha256_process_bytes (result, cnt, ctx); + /* Add for any character in the key one byte of the alternate sum. */ + for (cnt = key_len; cnt > 32; cnt -= 32) + sha256_process_bytes (alt_result, 32, &ctx); + sha256_process_bytes (alt_result, cnt, &ctx); - /* Take the binary representation of the length of the phrase and for every - 1 add the alternate sum, for every 0 the phrase. */ - for (cnt = phr_size; cnt > 0; cnt >>= 1) + /* Take the binary representation of the length of the key and for every + 1 add the alternate sum, for every 0 the key. */ + for (cnt = key_len; cnt > 0; cnt >>= 1) if ((cnt & 1) != 0) - sha256_process_bytes (result, 32, ctx); + sha256_process_bytes (alt_result, 32, &ctx); else - sha256_process_bytes (phrase, phr_size, ctx); + sha256_process_bytes (key, key_len, &ctx); /* Create intermediate result. */ - sha256_finish_ctx (ctx, result); + sha256_finish_ctx (&ctx, alt_result); /* Start computation of P byte sequence. */ - sha256_init_ctx (ctx); + sha256_init_ctx (&alt_ctx); /* For every character in the password add the entire password. */ - for (cnt = 0; cnt < phr_size; ++cnt) - sha256_process_bytes (phrase, phr_size, ctx); + for (cnt = 0; cnt < key_len; ++cnt) + sha256_process_bytes (key, key_len, &alt_ctx); /* Finish the digest. */ - sha256_finish_ctx (ctx, p_bytes); + sha256_finish_ctx (&alt_ctx, temp_result); + + /* Create byte sequence P. */ + cp = p_bytes = alloca (key_len); + for (cnt = key_len; cnt >= 32; cnt -= 32) + cp = mempcpy (cp, temp_result, 32); + memcpy (cp, temp_result, cnt); /* Start computation of S byte sequence. */ - sha256_init_ctx (ctx); + sha256_init_ctx (&alt_ctx); /* For every character in the password add the entire password. */ - for (cnt = 0; cnt < (size_t) 16 + (size_t) result[0]; ++cnt) - sha256_process_bytes (salt, salt_size, ctx); + for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) + sha256_process_bytes (salt, salt_len, &alt_ctx); /* Finish the digest. */ - sha256_finish_ctx (ctx, s_bytes); + sha256_finish_ctx (&alt_ctx, temp_result); + + /* Create byte sequence S. */ + cp = s_bytes = alloca (salt_len); + for (cnt = salt_len; cnt >= 32; cnt -= 32) + cp = mempcpy (cp, temp_result, 32); + memcpy (cp, temp_result, cnt); /* Repeatedly run the collected hash value through SHA256 to burn CPU cycles. */ for (cnt = 0; cnt < rounds; ++cnt) { /* New context. */ - sha256_init_ctx (ctx); + sha256_init_ctx (&ctx); - /* Add phrase or last result. */ + /* Add key or last result. */ if ((cnt & 1) != 0) - sha256_process_recycled_bytes (p_bytes, phr_size, ctx); + sha256_process_bytes (p_bytes, key_len, &ctx); else - sha256_process_bytes (result, 32, ctx); + sha256_process_bytes (alt_result, 32, &ctx); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) - sha256_process_recycled_bytes (s_bytes, salt_size, ctx); + sha256_process_bytes (s_bytes, salt_len, &ctx); - /* Add phrase for numbers not divisible by 7. */ + /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) - sha256_process_recycled_bytes (p_bytes, phr_size, ctx); + sha256_process_bytes (p_bytes, key_len, &ctx); - /* Add phrase or last result. */ + /* Add key or last result. */ if ((cnt & 1) != 0) - sha256_process_bytes (result, 32, ctx); + sha256_process_bytes (alt_result, 32, &ctx); else - sha256_process_recycled_bytes (p_bytes, phr_size, ctx); + sha256_process_bytes (p_bytes, key_len, &ctx); /* Create intermediate result. */ - sha256_finish_ctx (ctx, result); + sha256_finish_ctx (&ctx, alt_result); } - /* Now we can construct the result string. It consists of four - parts, one of which is optional. We already know that there - is sufficient space at CP for the longest possible result string. */ - memcpy (cp, sha256_salt_prefix, sizeof (sha256_salt_prefix) - 1); - cp += sizeof (sha256_salt_prefix) - 1; + /* Now we can construct the result string. It consists of three + parts. */ + cp = stpncpy (buffer, sha256_salt_prefix, MAX (0, buflen)); + buflen -= sizeof (sha256_salt_prefix) - 1; if (rounds_custom) { - int n = snprintf (cp, - SHA256_HASH_LENGTH - (sizeof (sha256_salt_prefix) - 1), - "%s%zu$", sha256_rounds_prefix, rounds); + int n = snprintf (cp, MAX (0, buflen), "%s%zu$", + sha256_rounds_prefix, rounds); cp += n; + buflen -= n; } - memcpy (cp, salt, salt_size); - cp += salt_size; - *cp++ = '$'; - -#define b64_from_24bit(B2, B1, B0, N) \ - do { \ - unsigned int w = ((((unsigned int)(B2)) << 16) | \ - (((unsigned int)(B1)) << 8) | \ - ((unsigned int)(B0))); \ - int n = (N); \ - while (n-- > 0) \ - { \ - *cp++ = b64t[w & 0x3f]; \ - w >>= 6; \ - } \ + cp = stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len)); + buflen -= MIN ((size_t) MAX (0, buflen), salt_len); + + if (buflen > 0) + { + *cp++ = '$'; + --buflen; + } + +#define b64_from_24bit(B2, B1, B0, N) \ + do { \ + unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ + int n = (N); \ + while (n-- > 0 && buflen > 0) \ + { \ + *cp++ = b64t[w & 0x3f]; \ + --buflen; \ + w >>= 6; \ + } \ } while (0) - b64_from_24bit (result[0], result[10], result[20], 4); - b64_from_24bit (result[21], result[1], result[11], 4); - b64_from_24bit (result[12], result[22], result[2], 4); - b64_from_24bit (result[3], result[13], result[23], 4); - b64_from_24bit (result[24], result[4], result[14], 4); - b64_from_24bit (result[15], result[25], result[5], 4); - b64_from_24bit (result[6], result[16], result[26], 4); - b64_from_24bit (result[27], result[7], result[17], 4); - b64_from_24bit (result[18], result[28], result[8], 4); - b64_from_24bit (result[9], result[19], result[29], 4); - b64_from_24bit (0, result[31], result[30], 3); - - *cp = '\0'; + b64_from_24bit (alt_result[0], alt_result[10], alt_result[20], 4); + b64_from_24bit (alt_result[21], alt_result[1], alt_result[11], 4); + b64_from_24bit (alt_result[12], alt_result[22], alt_result[2], 4); + b64_from_24bit (alt_result[3], alt_result[13], alt_result[23], 4); + b64_from_24bit (alt_result[24], alt_result[4], alt_result[14], 4); + b64_from_24bit (alt_result[15], alt_result[25], alt_result[5], 4); + b64_from_24bit (alt_result[6], alt_result[16], alt_result[26], 4); + b64_from_24bit (alt_result[27], alt_result[7], alt_result[17], 4); + b64_from_24bit (alt_result[18], alt_result[28], alt_result[8], 4); + b64_from_24bit (alt_result[9], alt_result[19], alt_result[29], 4); + b64_from_24bit (0, alt_result[31], alt_result[30], 3); + if (buflen <= 0) + { + errno = ERANGE; + buffer = NULL; + } + else + *cp = '\0'; /* Terminate the string. */ + + /* Clear the buffer for the intermediate result so that people + attaching to processes or reading core dumps cannot get any + information. We do it in this way to clear correct_words[] + inside the SHA256 implementation as well. */ + sha256_init_ctx (&ctx); + sha256_finish_ctx (&ctx, alt_result); + memset (temp_result, '\0', sizeof (temp_result)); + memset (p_bytes, '\0', key_len); + memset (s_bytes, '\0', salt_len); + memset (&ctx, '\0', sizeof (ctx)); + memset (&alt_ctx, '\0', sizeof (alt_ctx)); + if (copied_key != NULL) + memset (copied_key, '\0', key_len); + if (copied_salt != NULL) + memset (copied_salt, '\0', salt_len); + + return buffer; } void From 40a0d88687d7944ae618b1f507e6a0adc532399c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Esser?= Date: Sat, 14 Jul 2018 21:00:10 +0200 Subject: [PATCH 04/10] crypt-sha256.c: Apply previous commits from Zack Weinberg This patch summarizes the changes from: * 4a1152b4f9bc900d0488b473e20daed95025f4b0 * ccc833b4b2c60cefd74cb91a05f1ce7628179657 * 118abc2c5c08542fe528a1193328b161a756db90 * 9483c648978378a9cd128382049a88055d5b2a61 * a67c7cd73c19592315ae184fe8dee155ef6ebd31 * 4856729bd75c78e4e1e4e35ab5afd7cef6c66afc * a2d558581c3cd9b3d62f916825405cebd708f268 * b46a39a57e4801f5c41b1bac207239b6ed8e31a5 * cbdf27e254b0a113f0bea87071177f083e3f2545 * 00ec9369ac4875a8e3903d6e41ae785bbc8e8b26 * 114d4200eaa572525089795cf7990c3dafd5028c * ed4be6afa7b34fe0d827cc3f7f9608de0d9325cd * a7f9df50cecec46bb8176382faa685ce35ca72be * 15d1e3bf3d259ea262f3d34e8c225a3abfabaa09 --- crypt-sha256.c | 326 ++++++++++++++++++++++++------------------------- 1 file changed, 162 insertions(+), 164 deletions(-) diff --git a/crypt-sha256.c b/crypt-sha256.c index c2954a4..e036a18 100644 --- a/crypt-sha256.c +++ b/crypt-sha256.c @@ -1,13 +1,15 @@ /* One way encryption based on the SHA256-based Unix crypt implementation. * * Written by Ulrich Drepper in 2007 [1]. - * To the extent possible under law, Ulrich Drepper has waived all + * Modified by Zack Weinberg in 2017, 2018. + * Composed by Björn Esser in 2018. + * To the extent possible under law, the named authors have waived all * copyright and related or neighboring rights to this work. * * See https://creativecommons.org/publicdomain/zero/1.0/ for further * details. * - * This file is an except from [2], lines 648 up to 909. + * This file is a modified except from [2], lines 648 up to 909. * * [1] https://www.akkadia.org/drepper/sha-crypt.html * [2] https://www.akkadia.org/drepper/SHA-crypt.txt @@ -39,8 +41,35 @@ static const char sha256_rounds_prefix[] = "rounds="; /* Maximum number of rounds. */ #define ROUNDS_MAX 999999999 +/* The maximum possible length of a SHA256-hashed password string, + including the terminating NUL character. Prefix (including its NUL) + + rounds tag ("rounds=$" = "rounds=\0") + strlen(ROUNDS_MAX) + + salt (up to SALT_LEN_MAX chars) + '$' + hash (43 chars). */ + +#define LENGTH_OF_NUMBER(n) (sizeof #n - 1) + +#define SHA256_HASH_LENGTH \ + (sizeof (sha256_salt_prefix) + sizeof (sha256_rounds_prefix) + \ + LENGTH_OF_NUMBER (ROUNDS_MAX) + SALT_LEN_MAX + 1 + 43) + +static_assert (SHA256_HASH_LENGTH <= CRYPT_OUTPUT_SIZE, + "CRYPT_OUTPUT_SIZE is too small for SHA256"); + +/* A sha256_buffer holds all of the sensitive intermediate data. */ +struct sha256_buffer +{ + struct sha256_ctx ctx; + uint8_t result[32]; + uint8_t p_bytes[32]; + uint8_t s_bytes[32]; +}; + +static_assert (sizeof (struct sha256_buffer) <= ALG_SPECIFIC_SIZE, + "ALG_SPECIFIC_SIZE is too small for SHA256"); + + /* Table with characters for base64 transformation. */ -static const char b64t[64] = +static const char b64t[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /* Subroutine of _xcrypt_crypt_sha256_rn: Feed CTX with LEN bytes of a @@ -56,23 +85,30 @@ sha256_process_recycled_bytes (unsigned char block[32], size_t len, sha256_process_bytes (block, cnt, ctx); } -static char * -sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen) +void +crypt_sha256_rn (const char *phrase, size_t phr_size, + const char *setting, size_t ARG_UNUSED (set_size), + uint8_t *output, size_t out_size, + void *scratch, size_t scr_size) { - unsigned char alt_result[32] - __attribute__ ((__aligned__ (__alignof__ (uint32_t)))); - unsigned char temp_result[32] - __attribute__ ((__aligned__ (__alignof__ (uint32_t)))); - struct sha256_ctx ctx; - struct sha256_ctx alt_ctx; - size_t salt_len; - size_t key_len; + /* This shouldn't ever happen, but... */ + if (out_size < SHA256_HASH_LENGTH + || scr_size < sizeof (struct sha256_buffer)) + { + errno = ERANGE; + return; + } + + struct sha256_buffer *buf = scratch; + struct sha256_ctx *ctx = &buf->ctx; + uint8_t *result = buf->result; + uint8_t *p_bytes = buf->p_bytes; + uint8_t *s_bytes = buf->s_bytes; + char *cp = (char *)output; + const char *salt = setting; + + size_t salt_size; size_t cnt; - char *cp; - char *copied_key = NULL; - char *copied_salt = NULL; - char *p_bytes; - char *s_bytes; /* Default number of rounds. */ size_t rounds = ROUNDS_DEFAULT; bool rounds_custom = false; @@ -87,214 +123,176 @@ sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen) == 0) { const char *num = salt + sizeof (sha256_rounds_prefix) - 1; - char *endp; - unsigned long int srounds = strtoul (num, &endp, 10); - if (*endp == '$') + /* Do not allow an explicit setting of zero rounds, nor of the + default number of rounds, nor leading zeroes on the rounds. */ + if (!(*num >= '1' && *num <= '9')) { - salt = endp + 1; - rounds = MAX (ROUNDS_MIN, MIN (srounds, ROUNDS_MAX)); - rounds_custom = true; + errno = EINVAL; + return; } - } - - salt_len = MIN (strcspn (salt, "$"), SALT_LEN_MAX); - key_len = strlen (key); - if ((key - (char *) 0) % __alignof__ (uint32_t) != 0) - { - char *tmp = (char *) alloca (key_len + __alignof__ (uint32_t)); - key = copied_key = - memcpy (tmp + __alignof__ (uint32_t) - - (tmp - (char *) 0) % __alignof__ (uint32_t), - key, key_len); + errno = 0; + char *endp; + rounds = strtoul (num, &endp, 10); + if (endp == num || *endp != '$' + || rounds < ROUNDS_MIN + || rounds > ROUNDS_MAX + || errno) + { + errno = EINVAL; + return; + } + salt = endp + 1; + rounds_custom = true; } - if ((salt - (char *) 0) % __alignof__ (uint32_t) != 0) + salt_size = strspn (salt, b64t); + if (salt[salt_size] && salt[salt_size] != '$') { - char *tmp = (char *) alloca (salt_len + __alignof__ (uint32_t)); - salt = copied_salt = - memcpy (tmp + __alignof__ (uint32_t) - - (tmp - (char *) 0) % __alignof__ (uint32_t), - salt, salt_len); + errno = EINVAL; + return; } + if (salt_size > SALT_LEN_MAX) + salt_size = SALT_LEN_MAX; - /* Prepare for the real work. */ - sha256_init_ctx (&ctx); + /* Compute alternate SHA256 sum with input PHRASE, SALT, and PHRASE. The + final result will be added to the first context. */ + sha256_init_ctx (ctx); - /* Add the key string. */ - sha256_process_bytes (key, key_len, &ctx); + /* Add phrase. */ + sha256_process_bytes (phrase, phr_size, ctx); - /* The last part is the salt string. This must be at most 16 - characters and it ends at the first `$' character (for - compatibility with existing implementations). */ - sha256_process_bytes (salt, salt_len, &ctx); + /* Add salt. */ + sha256_process_bytes (salt, salt_size, ctx); - /* Compute alternate SHA256 sum with input KEY, SALT, and KEY. The - final result will be added to the first context. */ - sha256_init_ctx (&alt_ctx); + /* Add phrase again. */ + sha256_process_bytes (phrase, phr_size, ctx); - /* Add key. */ - sha256_process_bytes (key, key_len, &alt_ctx); + /* Now get result of this (32 bytes). */ + sha256_finish_ctx (ctx, result); - /* Add salt. */ - sha256_process_bytes (salt, salt_len, &alt_ctx); + /* Prepare for the real work. */ + sha256_init_ctx (ctx); - /* Add key again. */ - sha256_process_bytes (key, key_len, &alt_ctx); + /* Add the phrase string. */ + sha256_process_bytes (phrase, phr_size, ctx); - /* Now get result of this (32 bytes) and add it to the other - context. */ - sha256_finish_ctx (&alt_ctx, alt_result); + /* The last part is the salt string. This must be at most 8 + characters and it ends at the first `$' character (for + compatibility with existing implementations). */ + sha256_process_bytes (salt, salt_size, ctx); - /* Add for any character in the key one byte of the alternate sum. */ - for (cnt = key_len; cnt > 32; cnt -= 32) - sha256_process_bytes (alt_result, 32, &ctx); - sha256_process_bytes (alt_result, cnt, &ctx); + /* Add for any character in the phrase one byte of the alternate sum. */ + for (cnt = phr_size; cnt > 32; cnt -= 32) + sha256_process_bytes (result, 32, ctx); + sha256_process_bytes (result, cnt, ctx); - /* Take the binary representation of the length of the key and for every - 1 add the alternate sum, for every 0 the key. */ - for (cnt = key_len; cnt > 0; cnt >>= 1) + /* Take the binary representation of the length of the phrase and for every + 1 add the alternate sum, for every 0 the phrase. */ + for (cnt = phr_size; cnt > 0; cnt >>= 1) if ((cnt & 1) != 0) - sha256_process_bytes (alt_result, 32, &ctx); + sha256_process_bytes (result, 32, ctx); else - sha256_process_bytes (key, key_len, &ctx); + sha256_process_bytes (phrase, phr_size, ctx); /* Create intermediate result. */ - sha256_finish_ctx (&ctx, alt_result); + sha256_finish_ctx (ctx, result); /* Start computation of P byte sequence. */ - sha256_init_ctx (&alt_ctx); + sha256_init_ctx (ctx); /* For every character in the password add the entire password. */ - for (cnt = 0; cnt < key_len; ++cnt) - sha256_process_bytes (key, key_len, &alt_ctx); + for (cnt = 0; cnt < phr_size; ++cnt) + sha256_process_bytes (phrase, phr_size, ctx); /* Finish the digest. */ - sha256_finish_ctx (&alt_ctx, temp_result); - - /* Create byte sequence P. */ - cp = p_bytes = alloca (key_len); - for (cnt = key_len; cnt >= 32; cnt -= 32) - cp = mempcpy (cp, temp_result, 32); - memcpy (cp, temp_result, cnt); + sha256_finish_ctx (ctx, p_bytes); /* Start computation of S byte sequence. */ - sha256_init_ctx (&alt_ctx); + sha256_init_ctx (ctx); /* For every character in the password add the entire password. */ - for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) - sha256_process_bytes (salt, salt_len, &alt_ctx); + for (cnt = 0; cnt < (size_t) 16 + (size_t) result[0]; ++cnt) + sha256_process_bytes (salt, salt_size, ctx); /* Finish the digest. */ - sha256_finish_ctx (&alt_ctx, temp_result); - - /* Create byte sequence S. */ - cp = s_bytes = alloca (salt_len); - for (cnt = salt_len; cnt >= 32; cnt -= 32) - cp = mempcpy (cp, temp_result, 32); - memcpy (cp, temp_result, cnt); + sha256_finish_ctx (ctx, s_bytes); /* Repeatedly run the collected hash value through SHA256 to burn CPU cycles. */ for (cnt = 0; cnt < rounds; ++cnt) { /* New context. */ - sha256_init_ctx (&ctx); + sha256_init_ctx (ctx); - /* Add key or last result. */ + /* Add phrase or last result. */ if ((cnt & 1) != 0) - sha256_process_bytes (p_bytes, key_len, &ctx); + sha256_process_recycled_bytes (p_bytes, phr_size, ctx); else - sha256_process_bytes (alt_result, 32, &ctx); + sha256_process_bytes (result, 32, ctx); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) - sha256_process_bytes (s_bytes, salt_len, &ctx); + sha256_process_recycled_bytes (s_bytes, salt_size, ctx); - /* Add key for numbers not divisible by 7. */ + /* Add phrase for numbers not divisible by 7. */ if (cnt % 7 != 0) - sha256_process_bytes (p_bytes, key_len, &ctx); + sha256_process_recycled_bytes (p_bytes, phr_size, ctx); - /* Add key or last result. */ + /* Add phrase or last result. */ if ((cnt & 1) != 0) - sha256_process_bytes (alt_result, 32, &ctx); + sha256_process_bytes (result, 32, ctx); else - sha256_process_bytes (p_bytes, key_len, &ctx); + sha256_process_recycled_bytes (p_bytes, phr_size, ctx); /* Create intermediate result. */ - sha256_finish_ctx (&ctx, alt_result); + sha256_finish_ctx (ctx, result); } - /* Now we can construct the result string. It consists of three - parts. */ - cp = stpncpy (buffer, sha256_salt_prefix, MAX (0, buflen)); - buflen -= sizeof (sha256_salt_prefix) - 1; + /* Now we can construct the result string. It consists of four + parts, one of which is optional. We already know that there + is sufficient space at CP for the longest possible result string. */ + memcpy (cp, sha256_salt_prefix, sizeof (sha256_salt_prefix) - 1); + cp += sizeof (sha256_salt_prefix) - 1; if (rounds_custom) { - int n = snprintf (cp, MAX (0, buflen), "%s%zu$", - sha256_rounds_prefix, rounds); + int n = snprintf (cp, + SHA256_HASH_LENGTH - (sizeof (sha256_salt_prefix) - 1), + "%s%zu$", sha256_rounds_prefix, rounds); cp += n; - buflen -= n; - } - - cp = stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len)); - buflen -= MIN ((size_t) MAX (0, buflen), salt_len); - - if (buflen > 0) - { - *cp++ = '$'; - --buflen; } -#define b64_from_24bit(B2, B1, B0, N) \ - do { \ - unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ - int n = (N); \ - while (n-- > 0 && buflen > 0) \ - { \ - *cp++ = b64t[w & 0x3f]; \ - --buflen; \ - w >>= 6; \ - } \ + memcpy (cp, salt, salt_size); + cp += salt_size; + *cp++ = '$'; + +#define b64_from_24bit(B2, B1, B0, N) \ + do { \ + unsigned int w = ((((unsigned int)(B2)) << 16) | \ + (((unsigned int)(B1)) << 8) | \ + ((unsigned int)(B0))); \ + int n = (N); \ + while (n-- > 0) \ + { \ + *cp++ = b64t[w & 0x3f]; \ + w >>= 6; \ + } \ } while (0) - b64_from_24bit (alt_result[0], alt_result[10], alt_result[20], 4); - b64_from_24bit (alt_result[21], alt_result[1], alt_result[11], 4); - b64_from_24bit (alt_result[12], alt_result[22], alt_result[2], 4); - b64_from_24bit (alt_result[3], alt_result[13], alt_result[23], 4); - b64_from_24bit (alt_result[24], alt_result[4], alt_result[14], 4); - b64_from_24bit (alt_result[15], alt_result[25], alt_result[5], 4); - b64_from_24bit (alt_result[6], alt_result[16], alt_result[26], 4); - b64_from_24bit (alt_result[27], alt_result[7], alt_result[17], 4); - b64_from_24bit (alt_result[18], alt_result[28], alt_result[8], 4); - b64_from_24bit (alt_result[9], alt_result[19], alt_result[29], 4); - b64_from_24bit (0, alt_result[31], alt_result[30], 3); - if (buflen <= 0) - { - errno = ERANGE; - buffer = NULL; - } - else - *cp = '\0'; /* Terminate the string. */ - - /* Clear the buffer for the intermediate result so that people - attaching to processes or reading core dumps cannot get any - information. We do it in this way to clear correct_words[] - inside the SHA256 implementation as well. */ - sha256_init_ctx (&ctx); - sha256_finish_ctx (&ctx, alt_result); - memset (temp_result, '\0', sizeof (temp_result)); - memset (p_bytes, '\0', key_len); - memset (s_bytes, '\0', salt_len); - memset (&ctx, '\0', sizeof (ctx)); - memset (&alt_ctx, '\0', sizeof (alt_ctx)); - if (copied_key != NULL) - memset (copied_key, '\0', key_len); - if (copied_salt != NULL) - memset (copied_salt, '\0', salt_len); - - return buffer; + b64_from_24bit (result[0], result[10], result[20], 4); + b64_from_24bit (result[21], result[1], result[11], 4); + b64_from_24bit (result[12], result[22], result[2], 4); + b64_from_24bit (result[3], result[13], result[23], 4); + b64_from_24bit (result[24], result[4], result[14], 4); + b64_from_24bit (result[15], result[25], result[5], 4); + b64_from_24bit (result[6], result[16], result[26], 4); + b64_from_24bit (result[27], result[7], result[17], 4); + b64_from_24bit (result[18], result[28], result[8], 4); + b64_from_24bit (result[9], result[19], result[29], 4); + b64_from_24bit (0, result[31], result[30], 3); + + *cp = '\0'; } void From 989f1faae098a9345855bf733b8f35f92cefcf23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Esser?= Date: Sat, 14 Jul 2018 21:40:34 +0200 Subject: [PATCH 05/10] Replace crypt-sha512.c with an implementation in the Public Domain. This implementation is based on the SHA512-based Unix crypt implementation. Released into the Public Domain [1] by Ulrich Drepper . This file is a modified except from [2], lines 1403 up to 1676. [1] https://www.akkadia.org/drepper/sha-crypt.html [2] https://www.akkadia.org/drepper/SHA-crypt.txt --- crypt-sha512.c | 383 ++++++++++++++++++++++++------------------------- 1 file changed, 191 insertions(+), 192 deletions(-) diff --git a/crypt-sha512.c b/crypt-sha512.c index 955a27b..f3798a0 100644 --- a/crypt-sha512.c +++ b/crypt-sha512.c @@ -1,20 +1,17 @@ -/* One way encryption based on SHA512 sum. - - Copyright (C) 2007-2017 Free Software Foundation, Inc. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 of - the License, or (at your option) any later version. - - This library 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, see - . */ +/* One way encryption based on the SHA512-based Unix crypt implementation. + * + * Written by Ulrich Drepper in 2007 [1]. + * To the extent possible under law, Ulrich Drepper has waived all + * copyright and related or neighboring rights to this work. + * + * See https://creativecommons.org/publicdomain/zero/1.0/ for further + * details. + * + * This file is a modified except from [2], lines 1403 up to 1676. + * + * [1] https://www.akkadia.org/drepper/sha-crypt.html + * [2] https://www.akkadia.org/drepper/SHA-crypt.txt + */ #include "crypt-port.h" #include "crypt-private.h" @@ -42,36 +39,9 @@ static const char sha512_rounds_prefix[] = "rounds="; /* Maximum number of rounds. */ #define ROUNDS_MAX 999999999 -/* The maximum possible length of a SHA512-hashed password string, - including the terminating NUL character. Prefix (including its NUL) - + rounds tag ("rounds=$" = "rounds=\0") + strlen(ROUNDS_MAX) - + salt (up to SALT_LEN_MAX chars) + '$' + hash (86 chars). */ - -#define LENGTH_OF_NUMBER(n) (sizeof #n - 1) - -#define SHA512_HASH_LENGTH \ - (sizeof (sha512_salt_prefix) + sizeof (sha512_rounds_prefix) + \ - LENGTH_OF_NUMBER (ROUNDS_MAX) + SALT_LEN_MAX + 1 + 86) - -static_assert (SHA512_HASH_LENGTH <= CRYPT_OUTPUT_SIZE, - "CRYPT_OUTPUT_SIZE is too small for SHA512"); - -/* A sha512_buffer holds all of the sensitive intermediate data. */ -struct sha512_buffer -{ - struct sha512_ctx ctx; - uint8_t result[64]; - uint8_t p_bytes[64]; - uint8_t s_bytes[64]; -}; - -static_assert (sizeof (struct sha512_buffer) <= ALG_SPECIFIC_SIZE, - "ALG_SPECIFIC_SIZE is too small for SHA512"); - - /* Table with characters for base64 transformation. */ -static const char b64t[] = - "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +static const char b64t[64] = +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /* Subroutine of _xcrypt_crypt_sha512_rn: Feed CTX with LEN bytes of a virtual byte sequence consisting of BLOCK repeated over and over @@ -86,30 +56,23 @@ sha512_process_recycled_bytes (unsigned char block[64], size_t len, sha512_process_bytes (block, cnt, ctx); } -void -crypt_sha512_rn (const char *phrase, size_t phr_size, - const char *setting, size_t ARG_UNUSED (set_size), - uint8_t *output, size_t out_size, - void *scratch, size_t scr_size) +static char * +sha512_crypt_r (const char *key, const char *salt, char *buffer, int buflen) { - /* This shouldn't ever happen, but... */ - if (out_size < SHA512_HASH_LENGTH - || scr_size < sizeof (struct sha512_buffer)) - { - errno = ERANGE; - return; - } - - struct sha512_buffer *buf = scratch; - struct sha512_ctx *ctx = &buf->ctx; - uint8_t *result = buf->result; - uint8_t *p_bytes = buf->p_bytes; - uint8_t *s_bytes = buf->s_bytes; - char *cp = (char *)output; - const char *salt = setting; - - size_t salt_size; + unsigned char alt_result[64] + __attribute__ ((__aligned__ (__alignof__ (uint64_t)))); + unsigned char temp_result[64] + __attribute__ ((__aligned__ (__alignof__ (uint64_t)))); + struct sha512_ctx ctx; + struct sha512_ctx alt_ctx; + size_t salt_len; + size_t key_len; size_t cnt; + char *cp; + char *copied_key = NULL; + char *copied_salt = NULL; + char *p_bytes; + char *s_bytes; /* Default number of rounds. */ size_t rounds = ROUNDS_DEFAULT; bool rounds_custom = false; @@ -124,191 +87,227 @@ crypt_sha512_rn (const char *phrase, size_t phr_size, == 0) { const char *num = salt + sizeof (sha512_rounds_prefix) - 1; - /* Do not allow an explicit setting of zero rounds, nor of the - default number of rounds, nor leading zeroes on the rounds. */ - if (!(*num >= '1' && *num <= '9')) - { - errno = EINVAL; - return; - } - - errno = 0; char *endp; - rounds = strtoul (num, &endp, 10); - if (endp == num || *endp != '$' - || rounds < ROUNDS_MIN - || rounds > ROUNDS_MAX - || errno) - { - errno = EINVAL; - return; - } - salt = endp + 1; - rounds_custom = true; + unsigned long int srounds = strtoul (num, &endp, 10); + if (*endp == '$') + { + salt = endp + 1; + rounds = MAX (ROUNDS_MIN, MIN (srounds, ROUNDS_MAX)); + rounds_custom = true; + } + } + + salt_len = MIN (strcspn (salt, "$"), SALT_LEN_MAX); + key_len = strlen (key); + + if ((key - (char *) 0) % __alignof__ (uint64_t) != 0) + { + char *tmp = (char *) alloca (key_len + __alignof__ (uint64_t)); + key = copied_key = + memcpy (tmp + __alignof__ (uint64_t) + - (tmp - (char *) 0) % __alignof__ (uint64_t), + key, key_len); } - salt_size = strspn (salt, b64t); - if (salt[salt_size] && salt[salt_size] != '$') + if ((salt - (char *) 0) % __alignof__ (uint64_t) != 0) { - errno = EINVAL; - return; + char *tmp = (char *) alloca (salt_len + __alignof__ (uint64_t)); + salt = copied_salt = + memcpy (tmp + __alignof__ (uint64_t) + - (tmp - (char *) 0) % __alignof__ (uint64_t), + salt, salt_len); } - if (salt_size > SALT_LEN_MAX) - salt_size = SALT_LEN_MAX; - phr_size = strlen (phrase); - /* Compute alternate SHA512 sum with input PHRASE, SALT, and PHRASE. The + /* Prepare for the real work. */ + sha512_init_ctx (&ctx); + + /* Add the key string. */ + sha512_process_bytes (key, key_len, &ctx); + + /* The last part is the salt string. This must be at most 16 + characters and it ends at the first `$' character (for + compatibility with existing implementations). */ + sha512_process_bytes (salt, salt_len, &ctx); + + + /* Compute alternate SHA512 sum with input KEY, SALT, and KEY. The final result will be added to the first context. */ - sha512_init_ctx (ctx); + sha512_init_ctx (&alt_ctx); - /* Add phrase. */ - sha512_process_bytes (phrase, phr_size, ctx); + /* Add key. */ + sha512_process_bytes (key, key_len, &alt_ctx); /* Add salt. */ - sha512_process_bytes (salt, salt_size, ctx); + sha512_process_bytes (salt, salt_len, &alt_ctx); - /* Add phrase again. */ - sha512_process_bytes (phrase, phr_size, ctx); + /* Add key again. */ + sha512_process_bytes (key, key_len, &alt_ctx); /* Now get result of this (64 bytes) and add it to the other context. */ - sha512_finish_ctx (ctx, result); - - /* Prepare for the real work. */ - sha512_init_ctx (ctx); + sha512_finish_ctx (&alt_ctx, alt_result); - /* Add the phrase string. */ - sha512_process_bytes (phrase, phr_size, ctx); - - /* The last part is the salt string. This must be at most 8 - characters and it ends at the first `$' character (for - compatibility with existing implementations). */ - sha512_process_bytes (salt, salt_size, ctx); + /* Add for any character in the key one byte of the alternate sum. */ + for (cnt = key_len; cnt > 64; cnt -= 64) + sha512_process_bytes (alt_result, 64, &ctx); + sha512_process_bytes (alt_result, cnt, &ctx); - /* Add for any character in the phrase one byte of the alternate sum. */ - for (cnt = phr_size; cnt > 64; cnt -= 64) - sha512_process_bytes (result, 64, ctx); - sha512_process_bytes (result, cnt, ctx); - - /* Take the binary representation of the length of the phrase and for every - 1 add the alternate sum, for every 0 the phrase. */ - for (cnt = phr_size; cnt > 0; cnt >>= 1) + /* Take the binary representation of the length of the key and for every + 1 add the alternate sum, for every 0 the key. */ + for (cnt = key_len; cnt > 0; cnt >>= 1) if ((cnt & 1) != 0) - sha512_process_bytes (result, 64, ctx); + sha512_process_bytes (alt_result, 64, &ctx); else - sha512_process_bytes (phrase, phr_size, ctx); + sha512_process_bytes (key, key_len, &ctx); /* Create intermediate result. */ - sha512_finish_ctx (ctx, result); + sha512_finish_ctx (&ctx, alt_result); /* Start computation of P byte sequence. */ - sha512_init_ctx (ctx); + sha512_init_ctx (&alt_ctx); /* For every character in the password add the entire password. */ - for (cnt = 0; cnt < phr_size; ++cnt) - sha512_process_bytes (phrase, phr_size, ctx); + for (cnt = 0; cnt < key_len; ++cnt) + sha512_process_bytes (key, key_len, &alt_ctx); /* Finish the digest. */ - sha512_finish_ctx (ctx, p_bytes); + sha512_finish_ctx (&alt_ctx, temp_result); + + /* Create byte sequence P. */ + cp = p_bytes = alloca (key_len); + for (cnt = key_len; cnt >= 64; cnt -= 64) + cp = mempcpy (cp, temp_result, 64); + memcpy (cp, temp_result, cnt); /* Start computation of S byte sequence. */ - sha512_init_ctx (ctx); + sha512_init_ctx (&alt_ctx); /* For every character in the password add the entire password. */ - for (cnt = 0; cnt < (size_t) 16 + (size_t) result[0]; ++cnt) - sha512_process_bytes (salt, salt_size, ctx); + for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) + sha512_process_bytes (salt, salt_len, &alt_ctx); /* Finish the digest. */ - sha512_finish_ctx (ctx, s_bytes); + sha512_finish_ctx (&alt_ctx, temp_result); + + /* Create byte sequence S. */ + cp = s_bytes = alloca (salt_len); + for (cnt = salt_len; cnt >= 64; cnt -= 64) + cp = mempcpy (cp, temp_result, 64); + memcpy (cp, temp_result, cnt); /* Repeatedly run the collected hash value through SHA512 to burn CPU cycles. */ for (cnt = 0; cnt < rounds; ++cnt) { /* New context. */ - sha512_init_ctx (ctx); + sha512_init_ctx (&ctx); - /* Add phrase or last result. */ + /* Add key or last result. */ if ((cnt & 1) != 0) - sha512_process_recycled_bytes (p_bytes, phr_size, ctx); + sha512_process_bytes (p_bytes, key_len, &ctx); else - sha512_process_bytes (result, 64, ctx); + sha512_process_bytes (alt_result, 64, &ctx); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) - sha512_process_recycled_bytes (s_bytes, salt_size, ctx); + sha512_process_bytes (s_bytes, salt_len, &ctx); - /* Add phrase for numbers not divisible by 7. */ + /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) - sha512_process_recycled_bytes (p_bytes, phr_size, ctx); + sha512_process_bytes (p_bytes, key_len, &ctx); - /* Add phrase or last result. */ + /* Add key or last result. */ if ((cnt & 1) != 0) - sha512_process_bytes (result, 64, ctx); + sha512_process_bytes (alt_result, 64, &ctx); else - sha512_process_recycled_bytes (p_bytes, phr_size, ctx); + sha512_process_bytes (p_bytes, key_len, &ctx); /* Create intermediate result. */ - sha512_finish_ctx (ctx, result); + sha512_finish_ctx (&ctx, alt_result); } - /* Now we can construct the result string. It consists of four - parts, one of which is optional. We already know that buflen is - at least sha512_hash_length, therefore none of the string bashing - below can overflow the buffer. */ - - memcpy (cp, sha512_salt_prefix, sizeof (sha512_salt_prefix) - 1); - cp += sizeof (sha512_salt_prefix) - 1; + /* Now we can construct the result string. It consists of three + parts. */ + cp = __stpncpy (buffer, sha512_salt_prefix, MAX (0, buflen)); + buflen -= sizeof (sha512_salt_prefix) - 1; if (rounds_custom) { - int n = snprintf (cp, - SHA512_HASH_LENGTH - (sizeof (sha512_salt_prefix) - 1), - "%s%zu$", sha512_rounds_prefix, rounds); + int n = snprintf (cp, MAX (0, buflen), "%s%zu$", + sha512_rounds_prefix, rounds); cp += n; + buflen -= n; } - memcpy (cp, salt, salt_size); - cp += salt_size; - *cp++ = '$'; - -#define b64_from_24bit(B2, B1, B0, N) \ - do { \ - unsigned int w = ((((unsigned int)(B2)) << 16) | \ - (((unsigned int)(B1)) << 8) | \ - ((unsigned int)(B0))); \ - int n = (N); \ - while (n-- > 0) \ - { \ - *cp++ = b64t[w & 0x3f]; \ - w >>= 6; \ - } \ + cp = __stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len)); + buflen -= MIN ((size_t) MAX (0, buflen), salt_len); + + if (buflen > 0) + { + *cp++ = '$'; + --buflen; + } + +#define b64_from_24bit(B2, B1, B0, N) \ + do { \ + unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ + int n = (N); \ + while (n-- > 0 && buflen > 0) \ + { \ + *cp++ = b64t[w & 0x3f]; \ + --buflen; \ + w >>= 6; \ + } \ } while (0) - b64_from_24bit (result[0], result[21], result[42], 4); - b64_from_24bit (result[22], result[43], result[1], 4); - b64_from_24bit (result[44], result[2], result[23], 4); - b64_from_24bit (result[3], result[24], result[45], 4); - b64_from_24bit (result[25], result[46], result[4], 4); - b64_from_24bit (result[47], result[5], result[26], 4); - b64_from_24bit (result[6], result[27], result[48], 4); - b64_from_24bit (result[28], result[49], result[7], 4); - b64_from_24bit (result[50], result[8], result[29], 4); - b64_from_24bit (result[9], result[30], result[51], 4); - b64_from_24bit (result[31], result[52], result[10], 4); - b64_from_24bit (result[53], result[11], result[32], 4); - b64_from_24bit (result[12], result[33], result[54], 4); - b64_from_24bit (result[34], result[55], result[13], 4); - b64_from_24bit (result[56], result[14], result[35], 4); - b64_from_24bit (result[15], result[36], result[57], 4); - b64_from_24bit (result[37], result[58], result[16], 4); - b64_from_24bit (result[59], result[17], result[38], 4); - b64_from_24bit (result[18], result[39], result[60], 4); - b64_from_24bit (result[40], result[61], result[19], 4); - b64_from_24bit (result[62], result[20], result[41], 4); - b64_from_24bit (0, 0, result[63], 2); - - *cp = '\0'; + b64_from_24bit (alt_result[0], alt_result[21], alt_result[42], 4); + b64_from_24bit (alt_result[22], alt_result[43], alt_result[1], 4); + b64_from_24bit (alt_result[44], alt_result[2], alt_result[23], 4); + b64_from_24bit (alt_result[3], alt_result[24], alt_result[45], 4); + b64_from_24bit (alt_result[25], alt_result[46], alt_result[4], 4); + b64_from_24bit (alt_result[47], alt_result[5], alt_result[26], 4); + b64_from_24bit (alt_result[6], alt_result[27], alt_result[48], 4); + b64_from_24bit (alt_result[28], alt_result[49], alt_result[7], 4); + b64_from_24bit (alt_result[50], alt_result[8], alt_result[29], 4); + b64_from_24bit (alt_result[9], alt_result[30], alt_result[51], 4); + b64_from_24bit (alt_result[31], alt_result[52], alt_result[10], 4); + b64_from_24bit (alt_result[53], alt_result[11], alt_result[32], 4); + b64_from_24bit (alt_result[12], alt_result[33], alt_result[54], 4); + b64_from_24bit (alt_result[34], alt_result[55], alt_result[13], 4); + b64_from_24bit (alt_result[56], alt_result[14], alt_result[35], 4); + b64_from_24bit (alt_result[15], alt_result[36], alt_result[57], 4); + b64_from_24bit (alt_result[37], alt_result[58], alt_result[16], 4); + b64_from_24bit (alt_result[59], alt_result[17], alt_result[38], 4); + b64_from_24bit (alt_result[18], alt_result[39], alt_result[60], 4); + b64_from_24bit (alt_result[40], alt_result[61], alt_result[19], 4); + b64_from_24bit (alt_result[62], alt_result[20], alt_result[41], 4); + b64_from_24bit (0, 0, alt_result[63], 2); + + if (buflen <= 0) + { + errno = ERANGE; + buffer = NULL; + } + else + *cp = '\0'; /* Terminate the string. */ + + /* Clear the buffer for the intermediate result so that people + attaching to processes or reading core dumps cannot get any + information. We do it in this way to clear correct_words[] + inside the SHA512 implementation as well. */ + sha512_init_ctx (&ctx); + sha512_finish_ctx (&ctx, alt_result); + memset (temp_result, '\0', sizeof (temp_result)); + memset (p_bytes, '\0', key_len); + memset (s_bytes, '\0', salt_len); + memset (&ctx, '\0', sizeof (ctx)); + memset (&alt_ctx, '\0', sizeof (alt_ctx)); + if (copied_key != NULL) + memset (copied_key, '\0', key_len); + if (copied_salt != NULL) + memset (copied_salt, '\0', salt_len); + + return buffer; } void From 16abeb10230f3762c2c820b43bdb262013f2e17a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Esser?= Date: Sat, 14 Jul 2018 21:44:21 +0200 Subject: [PATCH 06/10] crypt-sha512.c: Apply previous commits from Zack Weinberg This patch summarizes the changes from: * 4a1152b4f9bc900d0488b473e20daed95025f4b0 * ccc833b4b2c60cefd74cb91a05f1ce7628179657 * 118abc2c5c08542fe528a1193328b161a756db90 * 9483c648978378a9cd128382049a88055d5b2a61 * 4856729bd75c78e4e1e4e35ab5afd7cef6c66afc * a2d558581c3cd9b3d62f916825405cebd708f268 * b46a39a57e4801f5c41b1bac207239b6ed8e31a5 * cbdf27e254b0a113f0bea87071177f083e3f2545 * 00ec9369ac4875a8e3903d6e41ae785bbc8e8b26 * 114d4200eaa572525089795cf7990c3dafd5028c * ed4be6afa7b34fe0d827cc3f7f9608de0d9325cd * a7f9df50cecec46bb8176382faa685ce35ca72be * 15d1e3bf3d259ea262f3d34e8c225a3abfabaa09 --- crypt-sha512.c | 356 ++++++++++++++++++++++++------------------------- 1 file changed, 178 insertions(+), 178 deletions(-) diff --git a/crypt-sha512.c b/crypt-sha512.c index f3798a0..39ec0b5 100644 --- a/crypt-sha512.c +++ b/crypt-sha512.c @@ -1,7 +1,9 @@ /* One way encryption based on the SHA512-based Unix crypt implementation. * * Written by Ulrich Drepper in 2007 [1]. - * To the extent possible under law, Ulrich Drepper has waived all + * Modified by Zack Weinberg in 2017, 2018. + * Composed by Björn Esser in 2018. + * To the extent possible under law, the named authors have waived all * copyright and related or neighboring rights to this work. * * See https://creativecommons.org/publicdomain/zero/1.0/ for further @@ -39,9 +41,36 @@ static const char sha512_rounds_prefix[] = "rounds="; /* Maximum number of rounds. */ #define ROUNDS_MAX 999999999 +/* The maximum possible length of a SHA512-hashed password string, + including the terminating NUL character. Prefix (including its NUL) + + rounds tag ("rounds=$" = "rounds=\0") + strlen(ROUNDS_MAX) + + salt (up to SALT_LEN_MAX chars) + '$' + hash (86 chars). */ + +#define LENGTH_OF_NUMBER(n) (sizeof #n - 1) + +#define SHA512_HASH_LENGTH \ + (sizeof (sha512_salt_prefix) + sizeof (sha512_rounds_prefix) + \ + LENGTH_OF_NUMBER (ROUNDS_MAX) + SALT_LEN_MAX + 1 + 86) + +static_assert (SHA512_HASH_LENGTH <= CRYPT_OUTPUT_SIZE, + "CRYPT_OUTPUT_SIZE is too small for SHA512"); + +/* A sha512_buffer holds all of the sensitive intermediate data. */ +struct sha512_buffer +{ + struct sha512_ctx ctx; + uint8_t result[64]; + uint8_t p_bytes[64]; + uint8_t s_bytes[64]; +}; + +static_assert (sizeof (struct sha512_buffer) <= ALG_SPECIFIC_SIZE, + "ALG_SPECIFIC_SIZE is too small for SHA512"); + + /* Table with characters for base64 transformation. */ -static const char b64t[64] = -"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +static const char b64t[] = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /* Subroutine of _xcrypt_crypt_sha512_rn: Feed CTX with LEN bytes of a virtual byte sequence consisting of BLOCK repeated over and over @@ -56,23 +85,30 @@ sha512_process_recycled_bytes (unsigned char block[64], size_t len, sha512_process_bytes (block, cnt, ctx); } -static char * -sha512_crypt_r (const char *key, const char *salt, char *buffer, int buflen) +void +crypt_sha512_rn (const char *phrase, size_t phr_size, + const char *setting, size_t ARG_UNUSED (set_size), + uint8_t *output, size_t out_size, + void *scratch, size_t scr_size) { - unsigned char alt_result[64] - __attribute__ ((__aligned__ (__alignof__ (uint64_t)))); - unsigned char temp_result[64] - __attribute__ ((__aligned__ (__alignof__ (uint64_t)))); - struct sha512_ctx ctx; - struct sha512_ctx alt_ctx; - size_t salt_len; - size_t key_len; + /* This shouldn't ever happen, but... */ + if (out_size < SHA512_HASH_LENGTH + || scr_size < sizeof (struct sha512_buffer)) + { + errno = ERANGE; + return; + } + + struct sha512_buffer *buf = scratch; + struct sha512_ctx *ctx = &buf->ctx; + uint8_t *result = buf->result; + uint8_t *p_bytes = buf->p_bytes; + uint8_t *s_bytes = buf->s_bytes; + char *cp = (char *)output; + const char *salt = setting; + + size_t salt_size; size_t cnt; - char *cp; - char *copied_key = NULL; - char *copied_salt = NULL; - char *p_bytes; - char *s_bytes; /* Default number of rounds. */ size_t rounds = ROUNDS_DEFAULT; bool rounds_custom = false; @@ -87,227 +123,191 @@ sha512_crypt_r (const char *key, const char *salt, char *buffer, int buflen) == 0) { const char *num = salt + sizeof (sha512_rounds_prefix) - 1; + /* Do not allow an explicit setting of zero rounds, nor of the + default number of rounds, nor leading zeroes on the rounds. */ + if (!(*num >= '1' && *num <= '9')) + { + errno = EINVAL; + return; + } + + errno = 0; char *endp; - unsigned long int srounds = strtoul (num, &endp, 10); - if (*endp == '$') - { - salt = endp + 1; - rounds = MAX (ROUNDS_MIN, MIN (srounds, ROUNDS_MAX)); - rounds_custom = true; - } + rounds = strtoul (num, &endp, 10); + if (endp == num || *endp != '$' + || rounds < ROUNDS_MIN + || rounds > ROUNDS_MAX + || errno) + { + errno = EINVAL; + return; + } + salt = endp + 1; + rounds_custom = true; } - salt_len = MIN (strcspn (salt, "$"), SALT_LEN_MAX); - key_len = strlen (key); - - if ((key - (char *) 0) % __alignof__ (uint64_t) != 0) + salt_size = strspn (salt, b64t); + if (salt[salt_size] && salt[salt_size] != '$') { - char *tmp = (char *) alloca (key_len + __alignof__ (uint64_t)); - key = copied_key = - memcpy (tmp + __alignof__ (uint64_t) - - (tmp - (char *) 0) % __alignof__ (uint64_t), - key, key_len); + errno = EINVAL; + return; } + if (salt_size > SALT_LEN_MAX) + salt_size = SALT_LEN_MAX; + phr_size = strlen (phrase); - if ((salt - (char *) 0) % __alignof__ (uint64_t) != 0) - { - char *tmp = (char *) alloca (salt_len + __alignof__ (uint64_t)); - salt = copied_salt = - memcpy (tmp + __alignof__ (uint64_t) - - (tmp - (char *) 0) % __alignof__ (uint64_t), - salt, salt_len); - } - - /* Prepare for the real work. */ - sha512_init_ctx (&ctx); - - /* Add the key string. */ - sha512_process_bytes (key, key_len, &ctx); - - /* The last part is the salt string. This must be at most 16 - characters and it ends at the first `$' character (for - compatibility with existing implementations). */ - sha512_process_bytes (salt, salt_len, &ctx); - - - /* Compute alternate SHA512 sum with input KEY, SALT, and KEY. The + /* Compute alternate SHA512 sum with input PHRASE, SALT, and PHRASE. The final result will be added to the first context. */ - sha512_init_ctx (&alt_ctx); + sha512_init_ctx (ctx); - /* Add key. */ - sha512_process_bytes (key, key_len, &alt_ctx); + /* Add phrase. */ + sha512_process_bytes (phrase, phr_size, ctx); /* Add salt. */ - sha512_process_bytes (salt, salt_len, &alt_ctx); + sha512_process_bytes (salt, salt_size, ctx); - /* Add key again. */ - sha512_process_bytes (key, key_len, &alt_ctx); + /* Add phrase again. */ + sha512_process_bytes (phrase, phr_size, ctx); /* Now get result of this (64 bytes) and add it to the other context. */ - sha512_finish_ctx (&alt_ctx, alt_result); + sha512_finish_ctx (ctx, result); + + /* Prepare for the real work. */ + sha512_init_ctx (ctx); + + /* Add the phrase string. */ + sha512_process_bytes (phrase, phr_size, ctx); + + /* The last part is the salt string. This must be at most 8 + characters and it ends at the first `$' character (for + compatibility with existing implementations). */ + sha512_process_bytes (salt, salt_size, ctx); - /* Add for any character in the key one byte of the alternate sum. */ - for (cnt = key_len; cnt > 64; cnt -= 64) - sha512_process_bytes (alt_result, 64, &ctx); - sha512_process_bytes (alt_result, cnt, &ctx); + /* Add for any character in the phrase one byte of the alternate sum. */ + for (cnt = phr_size; cnt > 64; cnt -= 64) + sha512_process_bytes (result, 64, ctx); + sha512_process_bytes (result, cnt, ctx); - /* Take the binary representation of the length of the key and for every - 1 add the alternate sum, for every 0 the key. */ - for (cnt = key_len; cnt > 0; cnt >>= 1) + /* Take the binary representation of the length of the phrase and for every + 1 add the alternate sum, for every 0 the phrase. */ + for (cnt = phr_size; cnt > 0; cnt >>= 1) if ((cnt & 1) != 0) - sha512_process_bytes (alt_result, 64, &ctx); + sha512_process_bytes (result, 64, ctx); else - sha512_process_bytes (key, key_len, &ctx); + sha512_process_bytes (phrase, phr_size, ctx); /* Create intermediate result. */ - sha512_finish_ctx (&ctx, alt_result); + sha512_finish_ctx (ctx, result); /* Start computation of P byte sequence. */ - sha512_init_ctx (&alt_ctx); + sha512_init_ctx (ctx); /* For every character in the password add the entire password. */ - for (cnt = 0; cnt < key_len; ++cnt) - sha512_process_bytes (key, key_len, &alt_ctx); + for (cnt = 0; cnt < phr_size; ++cnt) + sha512_process_bytes (phrase, phr_size, ctx); /* Finish the digest. */ - sha512_finish_ctx (&alt_ctx, temp_result); - - /* Create byte sequence P. */ - cp = p_bytes = alloca (key_len); - for (cnt = key_len; cnt >= 64; cnt -= 64) - cp = mempcpy (cp, temp_result, 64); - memcpy (cp, temp_result, cnt); + sha512_finish_ctx (ctx, p_bytes); /* Start computation of S byte sequence. */ - sha512_init_ctx (&alt_ctx); + sha512_init_ctx (ctx); /* For every character in the password add the entire password. */ - for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) - sha512_process_bytes (salt, salt_len, &alt_ctx); + for (cnt = 0; cnt < (size_t) 16 + (size_t) result[0]; ++cnt) + sha512_process_bytes (salt, salt_size, ctx); /* Finish the digest. */ - sha512_finish_ctx (&alt_ctx, temp_result); - - /* Create byte sequence S. */ - cp = s_bytes = alloca (salt_len); - for (cnt = salt_len; cnt >= 64; cnt -= 64) - cp = mempcpy (cp, temp_result, 64); - memcpy (cp, temp_result, cnt); + sha512_finish_ctx (ctx, s_bytes); /* Repeatedly run the collected hash value through SHA512 to burn CPU cycles. */ for (cnt = 0; cnt < rounds; ++cnt) { /* New context. */ - sha512_init_ctx (&ctx); + sha512_init_ctx (ctx); - /* Add key or last result. */ + /* Add phrase or last result. */ if ((cnt & 1) != 0) - sha512_process_bytes (p_bytes, key_len, &ctx); + sha512_process_recycled_bytes (p_bytes, phr_size, ctx); else - sha512_process_bytes (alt_result, 64, &ctx); + sha512_process_bytes (result, 64, ctx); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) - sha512_process_bytes (s_bytes, salt_len, &ctx); + sha512_process_recycled_bytes (s_bytes, salt_size, ctx); - /* Add key for numbers not divisible by 7. */ + /* Add phrase for numbers not divisible by 7. */ if (cnt % 7 != 0) - sha512_process_bytes (p_bytes, key_len, &ctx); + sha512_process_recycled_bytes (p_bytes, phr_size, ctx); - /* Add key or last result. */ + /* Add phrase or last result. */ if ((cnt & 1) != 0) - sha512_process_bytes (alt_result, 64, &ctx); + sha512_process_bytes (result, 64, ctx); else - sha512_process_bytes (p_bytes, key_len, &ctx); + sha512_process_recycled_bytes (p_bytes, phr_size, ctx); /* Create intermediate result. */ - sha512_finish_ctx (&ctx, alt_result); + sha512_finish_ctx (ctx, result); } - /* Now we can construct the result string. It consists of three - parts. */ - cp = __stpncpy (buffer, sha512_salt_prefix, MAX (0, buflen)); - buflen -= sizeof (sha512_salt_prefix) - 1; + /* Now we can construct the result string. It consists of four + parts, one of which is optional. We already know that buflen is + at least sha512_hash_length, therefore none of the string bashing + below can overflow the buffer. */ + + memcpy (cp, sha512_salt_prefix, sizeof (sha512_salt_prefix) - 1); + cp += sizeof (sha512_salt_prefix) - 1; if (rounds_custom) { - int n = snprintf (cp, MAX (0, buflen), "%s%zu$", - sha512_rounds_prefix, rounds); + int n = snprintf (cp, + SHA512_HASH_LENGTH - (sizeof (sha512_salt_prefix) - 1), + "%s%zu$", sha512_rounds_prefix, rounds); cp += n; - buflen -= n; - } - - cp = __stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len)); - buflen -= MIN ((size_t) MAX (0, buflen), salt_len); - - if (buflen > 0) - { - *cp++ = '$'; - --buflen; } -#define b64_from_24bit(B2, B1, B0, N) \ - do { \ - unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ - int n = (N); \ - while (n-- > 0 && buflen > 0) \ - { \ - *cp++ = b64t[w & 0x3f]; \ - --buflen; \ - w >>= 6; \ - } \ + memcpy (cp, salt, salt_size); + cp += salt_size; + *cp++ = '$'; + +#define b64_from_24bit(B2, B1, B0, N) \ + do { \ + unsigned int w = ((((unsigned int)(B2)) << 16) | \ + (((unsigned int)(B1)) << 8) | \ + ((unsigned int)(B0))); \ + int n = (N); \ + while (n-- > 0) \ + { \ + *cp++ = b64t[w & 0x3f]; \ + w >>= 6; \ + } \ } while (0) - b64_from_24bit (alt_result[0], alt_result[21], alt_result[42], 4); - b64_from_24bit (alt_result[22], alt_result[43], alt_result[1], 4); - b64_from_24bit (alt_result[44], alt_result[2], alt_result[23], 4); - b64_from_24bit (alt_result[3], alt_result[24], alt_result[45], 4); - b64_from_24bit (alt_result[25], alt_result[46], alt_result[4], 4); - b64_from_24bit (alt_result[47], alt_result[5], alt_result[26], 4); - b64_from_24bit (alt_result[6], alt_result[27], alt_result[48], 4); - b64_from_24bit (alt_result[28], alt_result[49], alt_result[7], 4); - b64_from_24bit (alt_result[50], alt_result[8], alt_result[29], 4); - b64_from_24bit (alt_result[9], alt_result[30], alt_result[51], 4); - b64_from_24bit (alt_result[31], alt_result[52], alt_result[10], 4); - b64_from_24bit (alt_result[53], alt_result[11], alt_result[32], 4); - b64_from_24bit (alt_result[12], alt_result[33], alt_result[54], 4); - b64_from_24bit (alt_result[34], alt_result[55], alt_result[13], 4); - b64_from_24bit (alt_result[56], alt_result[14], alt_result[35], 4); - b64_from_24bit (alt_result[15], alt_result[36], alt_result[57], 4); - b64_from_24bit (alt_result[37], alt_result[58], alt_result[16], 4); - b64_from_24bit (alt_result[59], alt_result[17], alt_result[38], 4); - b64_from_24bit (alt_result[18], alt_result[39], alt_result[60], 4); - b64_from_24bit (alt_result[40], alt_result[61], alt_result[19], 4); - b64_from_24bit (alt_result[62], alt_result[20], alt_result[41], 4); - b64_from_24bit (0, 0, alt_result[63], 2); - - if (buflen <= 0) - { - errno = ERANGE; - buffer = NULL; - } - else - *cp = '\0'; /* Terminate the string. */ - - /* Clear the buffer for the intermediate result so that people - attaching to processes or reading core dumps cannot get any - information. We do it in this way to clear correct_words[] - inside the SHA512 implementation as well. */ - sha512_init_ctx (&ctx); - sha512_finish_ctx (&ctx, alt_result); - memset (temp_result, '\0', sizeof (temp_result)); - memset (p_bytes, '\0', key_len); - memset (s_bytes, '\0', salt_len); - memset (&ctx, '\0', sizeof (ctx)); - memset (&alt_ctx, '\0', sizeof (alt_ctx)); - if (copied_key != NULL) - memset (copied_key, '\0', key_len); - if (copied_salt != NULL) - memset (copied_salt, '\0', salt_len); - - return buffer; + b64_from_24bit (result[0], result[21], result[42], 4); + b64_from_24bit (result[22], result[43], result[1], 4); + b64_from_24bit (result[44], result[2], result[23], 4); + b64_from_24bit (result[3], result[24], result[45], 4); + b64_from_24bit (result[25], result[46], result[4], 4); + b64_from_24bit (result[47], result[5], result[26], 4); + b64_from_24bit (result[6], result[27], result[48], 4); + b64_from_24bit (result[28], result[49], result[7], 4); + b64_from_24bit (result[50], result[8], result[29], 4); + b64_from_24bit (result[9], result[30], result[51], 4); + b64_from_24bit (result[31], result[52], result[10], 4); + b64_from_24bit (result[53], result[11], result[32], 4); + b64_from_24bit (result[12], result[33], result[54], 4); + b64_from_24bit (result[34], result[55], result[13], 4); + b64_from_24bit (result[56], result[14], result[35], 4); + b64_from_24bit (result[15], result[36], result[57], 4); + b64_from_24bit (result[37], result[58], result[16], 4); + b64_from_24bit (result[59], result[17], result[38], 4); + b64_from_24bit (result[18], result[39], result[60], 4); + b64_from_24bit (result[40], result[61], result[19], 4); + b64_from_24bit (result[62], result[20], result[41], 4); + b64_from_24bit (0, 0, result[63], 2); + + *cp = '\0'; } void From c76847e3be40c4ac0d78bc8518502418c6207144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Esser?= Date: Wed, 1 Aug 2018 19:34:53 +0200 Subject: [PATCH 07/10] Update LICENSING and NEWS for changes in previous commits. crypt-sha{256,512}.c have been replaced with an implementation in the Public Domain. --- LICENSING | 7 +++++-- NEWS | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/LICENSING b/LICENSING index b9df920..3efbed0 100644 --- a/LICENSING +++ b/LICENSING @@ -14,14 +14,17 @@ source tree. For specific licensing terms consult the files themselves. * Copyright Free Software Foundation, Inc.; LGPL (v2.1 or later): crypt-base.h, crypt-obsolete.h, crypt-private.h alg-md5.h, alg-md5.c, crypt-md5.c, - alg-sha256.h, alg-sha256.c, crypt-sha256.c, - alg-sha512.h, alg-sha512.c, crypt-sha256.c, + alg-sha256.h, alg-sha256.c, + alg-sha512.h, alg-sha512.c, test-crypt-badsalt, test-crypt-nonnull * Copyright David Burren et al.; 3-clause BSD: alg-des.h, alg-des.c, alg-des-tables.c, crypt-des.c, crypt-des-obsolete.c, gen-des-tables.c + * Public domain, written by Ulrich Drepper et al.: + crypt-sha256.c, crypt-sha512.c + * Public domain, written by Solar Designer et al.: alg-md4.h, alg-md4.c, crypt-bcrypt.c, crypt-gensalt.c, test-crypt-bcrypt.c diff --git a/NEWS b/NEWS index 77b556f..8acf032 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ Please send bug reports, questions and suggestions to Version 4.1.2 * Add optional 'check-valgrind' target to the Makefile. +* Replace crypt-sha{256,512}.c with an implementation in the Public + Domain. Version 4.1.1 * --enable-hashes now supports additional groups of hashing methods: From cc1806e214b89403152c2c53932d8d0b8aeb1e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Esser?= Date: Sat, 4 Aug 2018 13:02:03 +0200 Subject: [PATCH 08/10] Add alias man-pages for other crypt functions. --- Makefile.am | 3 ++- crypt.3 | 1 + crypt.5 | 2 ++ crypt_gensalt.3 | 2 ++ crypt_r.3 | 1 + crypt_ra.3 | 1 + 6 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 crypt.3 create mode 100644 crypt_r.3 create mode 100644 crypt_ra.3 diff --git a/Makefile.am b/Makefile.am index 874db12..41ca783 100644 --- a/Makefile.am +++ b/Makefile.am @@ -18,7 +18,8 @@ EXTRA_DIST = \ gen-map.awk gen-vers.awk gen-crypt-h.awk \ gen-hashes.awk sel-hashes.awk hashes.lst -notrans_dist_man3_MANS = crypt_rn.3 crypt_gensalt.3 +notrans_dist_man3_MANS = crypt.3 crypt_r.3 crypt_ra.3 \ + crypt_rn.3 crypt_gensalt.3 notrans_dist_man5_MANS = crypt.5 nodist_include_HEADERS = crypt.h diff --git a/crypt.3 b/crypt.3 new file mode 100644 index 0000000..430e48f --- /dev/null +++ b/crypt.3 @@ -0,0 +1 @@ +.so man3/crypt_rn.3 diff --git a/crypt.5 b/crypt.5 index 5db9c92..7fe4609 100644 --- a/crypt.5 +++ b/crypt.5 @@ -279,6 +279,8 @@ that will work on an old operating system that supports nothing else. .hash "$3$" "\e$3\e$\e$[0-9a-f]{32}" unlimited 8 256 256 0 1 .SH SEE ALSO .BR crypt (3), +.BR crypt_r (3), +.BR crypt_ra (3), .BR crypt_rn (3), .BR crypt_gensalt (3), .BR getpwent (3), diff --git a/crypt_gensalt.3 b/crypt_gensalt.3 index ebfff28..3109740 100644 --- a/crypt_gensalt.3 +++ b/crypt_gensalt.3 @@ -223,6 +223,8 @@ T} Thread safety MT-Safe .SH SEE ALSO .ad l .BR crypt (3), +.BR crypt_r (3), +.BR crypt_ra (3), .BR crypt_rn (3), .BR getpass (3), .BR getpwent (3), diff --git a/crypt_r.3 b/crypt_r.3 new file mode 100644 index 0000000..430e48f --- /dev/null +++ b/crypt_r.3 @@ -0,0 +1 @@ +.so man3/crypt_rn.3 diff --git a/crypt_ra.3 b/crypt_ra.3 new file mode 100644 index 0000000..430e48f --- /dev/null +++ b/crypt_ra.3 @@ -0,0 +1 @@ +.so man3/crypt_rn.3 From 6679d3427645da43140e5c5297f46985590b8e08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Esser?= Date: Sat, 4 Aug 2018 20:45:42 +0200 Subject: [PATCH 09/10] Update NEWS. --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 8acf032..7e1d526 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ Version 4.1.2 * Add optional 'check-valgrind' target to the Makefile. * Replace crypt-sha{256,512}.c with an implementation in the Public Domain. +* Add alias man-pages for other crypt functions. Version 4.1.1 * --enable-hashes now supports additional groups of hashing methods: From 8596e298f761c32cecff45424f5242cd14269292 Mon Sep 17 00:00:00 2001 From: Zack Weinberg Date: Tue, 7 Aug 2018 21:35:12 -0400 Subject: [PATCH 10/10] Add configure option --disable-failure-tokens. When this option is given, crypt and crypt_r will return NULL on failure, instead of a special "failure token" string that isn't the hash of any passphrase. This was the historical behavior of glibc, FreeBSD libc, and several other implementations. --- NEWS | 8 ++++++ configure.ac | 20 +++++++++++++ crypt.c | 4 +++ crypt_rn.3 | 35 +++++++++++++---------- test-badsalt.c | 18 +++++++++++- test-crypt-badargs.c | 68 +++++++++++++++++++++++++------------------- test-crypt-bcrypt.c | 6 +++- 7 files changed, 112 insertions(+), 47 deletions(-) diff --git a/NEWS b/NEWS index 7e1d526..de91957 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,14 @@ Version 4.1.2 * Replace crypt-sha{256,512}.c with an implementation in the Public Domain. * Add alias man-pages for other crypt functions. +* Add configure option --disable-failure-tokens, which causes crypt + and crypt_r to return NULL on failure, as crypt_rn and crypt_ra do, + instead of a special "failure token". Using this option improves + compatibility with programs written on the assumption that, like + most C library functions, crypt and crypt_r will return NULL on + failure; but it breaks compatibility with programs that assume these + functions never return NULL. We're not sure which type of program + is more common. Please let us know if you encounter either. Version 4.1.1 * --enable-hashes now supports additional groups of hashing methods: diff --git a/configure.ac b/configure.ac index 9ea30f6..84d3ad9 100644 --- a/configure.ac +++ b/configure.ac @@ -162,6 +162,26 @@ AX_VALGRIND_DFLT([sgcheck], [off]) AX_VALGRIND_CHECK() # Configure options. +AC_ARG_ENABLE([failure-tokens], + AS_HELP_STRING( + [--disable-failure-tokens], + [Make crypt and crypt_r return NULL on failure, instead of a + special "failure token" string that isn't the hash of any + passphrase. This matches the behavior of several other + crypt implementations, but will break programs that assume these + functions never return NULL. crypt_rn and crypt_ra are not affected + by this option, and will always return NULL on failure.] + ), + [case "$enableval" in + yes) enable_failure_tokens=1;; + no) enable_failure_tokens=0;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-failure-tokens]);; + esac], + [enable_failure_tokens=1]) +AC_DEFINE_UNQUOTED([ENABLE_FAILURE_TOKENS], [$enable_failure_tokens], + [Define to 1 if crypt and crypt_r should return a "failure token" on + failure, or 0 if they should return NULL.]) + AC_ARG_ENABLE([obsolete-api], AS_HELP_STRING( [--enable-obsolete-api[=ARG]], diff --git a/crypt.c b/crypt.c index 9a3e192..839763a 100644 --- a/crypt.c +++ b/crypt.c @@ -235,7 +235,11 @@ crypt_r (const char *phrase, const char *setting, struct crypt_data *data) { make_failure_token (setting, data->output, sizeof data->output); do_crypt (phrase, setting, data); +#if ENABLE_FAILURE_TOKENS return data->output; +#else + return data->output[0] == '*' ? 0 : data->output; +#endif } SYMVER_crypt_r; #endif diff --git a/crypt_rn.3 b/crypt_rn.3 index 24da44c..d021c4e 100644 --- a/crypt_rn.3 +++ b/crypt_rn.3 @@ -204,17 +204,31 @@ multiple threads simultaneously, as long as a separate object is used for each thread. .PP Upon error, -.B crypt -and -.B crypt_r -return a pointer to an +.BR crypt_r ", " crypt_rn ", and " crypt_ra +write an .I invalid -hashed passphrase. +hashed passphrase to the +.I output +field of their +.I crypt_data +object, and +.B crypt +writes an invalid hash to its static storage area. This string will be shorter than 13 characters, will begin with a \(oq\fB*\fR\(cq, and will not compare equal to .IR setting . -(This peculiar behavior is for compatibility +.PP +Upon error, +.BR crypt_rn " and " crypt_ra +return a null pointer. +.BR crypt_r " and " crypt +may also return a null pointer, +or they may return a pointer to the invalid hash, +depending on how +.I libcrypt +was configured. +(The option to return the invalid hash is for compatibility with old applications that assume that .B crypt cannot return a null pointer. @@ -222,15 +236,6 @@ See .B "PORTABILITY NOTES" below.) .PP -.B crypt_rn -and -.B crypt_ra -also write an invalid hashed passphrase to the -.I output -field of their -.I crypt_data -object when they fail, but they return a null pointer. -.PP All four functions set .I errno when they fail. diff --git a/test-badsalt.c b/test-badsalt.c index b274337..3d2e47a 100644 --- a/test-badsalt.c +++ b/test-badsalt.c @@ -222,12 +222,28 @@ check_crypt (const char *label, const char *fn, const char *retval, const char *setting, bool expected_to_succeed) { - /* crypt/crypt_r should never return null */ +#if ENABLE_FAILURE_TOKENS + /* crypt/crypt_r never return null when failure tokens are enabled */ if (!retval) { printf ("FAIL: %s/%s/%s: returned NULL\n", label, setting, fn); return false; } +#else + if (expected_to_succeed && !retval) + { + printf ("FAIL: %s/%s/%s: returned NULL\n", label, setting, fn); + return false; + } + else if (!expected_to_succeed && retval) + { + printf ("FAIL: %s/%s/%s: returned %p, should be NULL\n", + label, setting, fn, (const void *)retval); + return false; + } + else if (!expected_to_succeed && !retval) + return true; +#endif if (!check_results (label, fn, retval, setting, expected_to_succeed)) return false; diff --git a/test-crypt-badargs.c b/test-crypt-badargs.c index 0e6af16..6be24a9 100644 --- a/test-crypt-badargs.c +++ b/test-crypt-badargs.c @@ -169,6 +169,14 @@ test_crypt_ra (const char *tag, check (tag, expect, got); } +#if ENABLE_FAILURE_TOKENS +# define FT0 "*0" +# define FT1 "*1" +#else +# define FT0 0 +# define FT1 0 +#endif + /* PAGE should point to PAGESIZE bytes of read-write memory followed by another PAGESIZE bytes of inaccessible memory. */ @@ -187,55 +195,55 @@ do_tests(char *page, size_t pagesize) size_t i; /* When SETTING is null, it shouldn't matter what PHRASE is. */ - expect_no_fault ("0.0.crypt", 0, 0, "*0", test_crypt); - expect_no_fault ("0.0.crypt_r", 0, 0, "*0", test_crypt_r); + expect_no_fault ("0.0.crypt", 0, 0, FT0, test_crypt); + expect_no_fault ("0.0.crypt_r", 0, 0, FT0, test_crypt_r); expect_no_fault ("0.0.crypt_rn", 0, 0, 0, test_crypt_rn); expect_no_fault ("0.0.crypt_ra", 0, 0, 0, test_crypt_ra); - expect_no_fault ("''.0.crypt", "", 0, "*0", test_crypt); - expect_no_fault ("''.0.crypt_r", "", 0, "*0", test_crypt_r); + expect_no_fault ("''.0.crypt", "", 0, FT0, test_crypt); + expect_no_fault ("''.0.crypt_r", "", 0, FT0, test_crypt_r); expect_no_fault ("''.0.crypt_rn", "", 0, 0, test_crypt_rn); expect_no_fault ("''.0.crypt_ra", "", 0, 0, test_crypt_ra); - expect_no_fault ("ph.0.crypt", phrase, 0, "*0", test_crypt); - expect_no_fault ("ph.0.crypt_r", phrase, 0, "*0", test_crypt_r); + expect_no_fault ("ph.0.crypt", phrase, 0, FT0, test_crypt); + expect_no_fault ("ph.0.crypt_r", phrase, 0, FT0, test_crypt_r); expect_no_fault ("ph.0.crypt_rn", phrase, 0, 0, test_crypt_rn); expect_no_fault ("ph.0.crypt_ra", phrase, 0, 0, test_crypt_ra); - expect_no_fault ("p1.0.crypt", p1, 0, "*0", test_crypt); - expect_no_fault ("p1.0.crypt_r", p1, 0, "*0", test_crypt_r); + expect_no_fault ("p1.0.crypt", p1, 0, FT0, test_crypt); + expect_no_fault ("p1.0.crypt_r", p1, 0, FT0, test_crypt_r); expect_no_fault ("p1.0.crypt_rn", p1, 0, 0, test_crypt_rn); expect_no_fault ("p1.0.crypt_ra", p1, 0, 0, test_crypt_ra); - expect_no_fault ("p2.0.crypt", p2, 0, "*0", test_crypt); - expect_no_fault ("p2.0.crypt_r", p2, 0, "*0", test_crypt_r); + expect_no_fault ("p2.0.crypt", p2, 0, FT0, test_crypt); + expect_no_fault ("p2.0.crypt_r", p2, 0, FT0, test_crypt_r); expect_no_fault ("p2.0.crypt_rn", p2, 0, 0, test_crypt_rn); expect_no_fault ("p2.0.crypt_ra", p2, 0, 0, test_crypt_ra); /* Conversely, when PHRASE is null, it shouldn't matter what SETTING is... */ - expect_no_fault ("0.''.crypt", 0, "", "*0", test_crypt); - expect_no_fault ("0.''.crypt_r", 0, "", "*0", test_crypt_r); + expect_no_fault ("0.''.crypt", 0, "", FT0, test_crypt); + expect_no_fault ("0.''.crypt_r", 0, "", FT0, test_crypt_r); expect_no_fault ("0.''.crypt_rn", 0, "", 0, test_crypt_rn); expect_no_fault ("0.''.crypt_ra", 0, "", 0, test_crypt_ra); - expect_no_fault ("0.'*'.crypt", 0, "*", "*0", test_crypt); - expect_no_fault ("0.'*'.crypt_r", 0, "*", "*0", test_crypt_r); + expect_no_fault ("0.'*'.crypt", 0, "*", FT0, test_crypt); + expect_no_fault ("0.'*'.crypt_r", 0, "*", FT0, test_crypt_r); expect_no_fault ("0.'*'.crypt_rn", 0, "*", 0, test_crypt_rn); expect_no_fault ("0.'*'.crypt_ra", 0, "*", 0, test_crypt_ra); - expect_no_fault ("0.'*0'.crypt", 0, "*0", "*1", test_crypt); - expect_no_fault ("0.'*0'.crypt_r", 0, "*0", "*1", test_crypt_r); + expect_no_fault ("0.'*0'.crypt", 0, "*0", FT1, test_crypt); + expect_no_fault ("0.'*0'.crypt_r", 0, "*0", FT1, test_crypt_r); expect_no_fault ("0.'*0'.crypt_rn", 0, "*0", 0, test_crypt_rn); expect_no_fault ("0.'*0'.crypt_ra", 0, "*0", 0, test_crypt_ra); - expect_no_fault ("0.'*1'.crypt", 0, "*1", "*0", test_crypt); - expect_no_fault ("0.'*1'.crypt_r", 0, "*1", "*0", test_crypt_r); + expect_no_fault ("0.'*1'.crypt", 0, "*1", FT0, test_crypt); + expect_no_fault ("0.'*1'.crypt_r", 0, "*1", FT0, test_crypt_r); expect_no_fault ("0.'*1'.crypt_rn", 0, "*1", 0, test_crypt_rn); expect_no_fault ("0.'*1'.crypt_ra", 0, "*1", 0, test_crypt_ra); - expect_no_fault ("0.p1.crypt", 0, p1, "*0", test_crypt); - expect_no_fault ("0.p1.crypt_r", 0, p1, "*0", test_crypt_r); + expect_no_fault ("0.p1.crypt", 0, p1, FT0, test_crypt); + expect_no_fault ("0.p1.crypt_r", 0, p1, FT0, test_crypt_r); expect_no_fault ("0.p1.crypt_rn", 0, p1, 0, test_crypt_rn); expect_no_fault ("0.p1.crypt_ra", 0, p1, 0, test_crypt_ra); @@ -245,8 +253,8 @@ do_tests(char *page, size_t pagesize) bug, but it's impractical to fix without breaking the property that 'crypt' _never_ creates a failure token that is equal to the setting string, which is more important than this corner case. */ - expect_a_fault ("0.p2.crypt", 0, p2, "*0", test_crypt); - expect_a_fault ("0.p2.crypt_r", 0, p2, "*0", test_crypt_r); + expect_a_fault ("0.p2.crypt", 0, p2, FT0, test_crypt); + expect_a_fault ("0.p2.crypt_r", 0, p2, FT0, test_crypt_r); expect_a_fault ("0.p2.crypt_rn", 0, p2, 0, test_crypt_rn); expect_a_fault ("0.p2.crypt_ra", 0, p2, 0, test_crypt_ra); @@ -257,9 +265,9 @@ do_tests(char *page, size_t pagesize) strcpy (page, "p1.'"); strcat (page, settings[i]); strcat (page, "'.crypt"); - expect_a_fault (page, p1, settings[i], "*0", test_crypt); + expect_a_fault (page, p1, settings[i], FT0, test_crypt); strcat (page, "_r"); - expect_a_fault (page, p1, settings[i], "*0", test_crypt_r); + expect_a_fault (page, p1, settings[i], FT0, test_crypt_r); strcat (page, "n"); expect_a_fault (page, p1, settings[i], 0, test_crypt_rn); page [strlen (page) - 1] = 'a'; @@ -268,9 +276,9 @@ do_tests(char *page, size_t pagesize) strcpy (page, "p2.'"); strcat (page, settings[i]); strcat (page, "'.crypt"); - expect_a_fault (page, p2, settings[i], "*0", test_crypt); + expect_a_fault (page, p2, settings[i], FT0, test_crypt); strcat (page, "_r"); - expect_a_fault (page, p2, settings[i], "*0", test_crypt_r); + expect_a_fault (page, p2, settings[i], FT0, test_crypt_r); strcat (page, "n"); expect_a_fault (page, p2, settings[i], 0, test_crypt_rn); page [strlen (page) - 1] = 'a'; @@ -279,8 +287,8 @@ do_tests(char *page, size_t pagesize) /* Conversely, when PHRASE is valid, passing an invalid string as SETTING should crash reliably. */ - expect_a_fault ("ph.p2.crypt", phrase, p2, "*0", test_crypt); - expect_a_fault ("ph.p2.crypt_r", phrase, p2, "*0", test_crypt_r); + expect_a_fault ("ph.p2.crypt", phrase, p2, FT0, test_crypt); + expect_a_fault ("ph.p2.crypt_r", phrase, p2, FT0, test_crypt_r); expect_a_fault ("ph.p2.crypt_rn", phrase, p2, 0, test_crypt_rn); expect_a_fault ("ph.p2.crypt_ra", phrase, p2, 0, test_crypt_ra); @@ -292,9 +300,9 @@ do_tests(char *page, size_t pagesize) strcpy (page, "ph.'"); strcat (page, settings[i]); strcat (page, ".crypt"); - expect_a_fault (page, phrase, p1, "*0", test_crypt); + expect_a_fault (page, phrase, p1, FT0, test_crypt); strcat (page, "_r"); - expect_a_fault (page, phrase, p1, "*0", test_crypt_r); + expect_a_fault (page, phrase, p1, FT0, test_crypt_r); strcat (page, "n"); expect_a_fault (page, phrase, p1, 0, test_crypt_rn); page [strlen (page) - 1] = 'a'; diff --git a/test-crypt-bcrypt.c b/test-crypt-bcrypt.c index c984e4d..bf149b4 100644 --- a/test-crypt-bcrypt.c +++ b/test-crypt-bcrypt.c @@ -194,8 +194,12 @@ main (void) errno = 0; p = crypt (key, setting); errnm = errno; +#if ENABLE_FAILURE_TOKENS match = strcmp (p, hash); - if ((!ok && !errno) || strcmp (p, hash)) +#else + match = (ok ? strcmp (p, hash) : p != 0); +#endif + if ((!ok && !errno) || match) { printf ("FAIL: %d/crypt.1: key=%s setting=%s: xhash=%s xerr=%d, " "p=%s match=%d err=%s\n",