Skip to content

Commit

Permalink
Merge pull request #484 from nichtsfrei/new_hash_method
Browse files Browse the repository at this point in the history
Introducing passwordbasedauthentication based on lcrypt and sha512
  • Loading branch information
nichtsfrei authored Apr 29, 2021
2 parents d517c4d + 1115330 commit a62c406
Show file tree
Hide file tree
Showing 8 changed files with 534 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
build/
.ccls
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added

- Add support for volatile keys. [#460](https://github.com/greenbone/gvm-libs/pull/460)

- Possibility to use lcrypt with `$6$` (sha512) for authentication [484](https://github.com/greenbone/gvm-libs/pull/484)
### Changed
### Fixed
### Removed
Expand Down
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,8 @@ if (BUILD_TESTS AND NOT SKIP_SRC)
add_custom_target (tests
DEPENDS array-test alivedetection-test boreas_error-test boreas_io-test
cli-test cvss-test ping-test sniffer-test util-test networking-test
xmlutils-test version-test osp-test nvti-test hosts-test)
passwordbasedauthentication-test xmlutils-test version-test osp-test
nvti-test hosts-test)

endif (BUILD_TESTS AND NOT SKIP_SRC)

Expand Down
2 changes: 1 addition & 1 deletion base/cvss_tests.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2009-2020 Greenbone Networks GmbH
/* Copyright (C) 2009-2021 Greenbone Networks GmbH
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
Expand Down
38 changes: 35 additions & 3 deletions util/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,21 @@ else (NOT GPGME)
endif (GPGME_VERSION VERSION_LESS GPGME_MIN_VERSION)
endif (NOT GPGME)


message (STATUS "Looking for libcrypt...")
find_library (CRYPT crypt)
message (STATUS "Looking for libcrypt... ${CRYPT}")
if (NOT CRYPT)
message (SEND_ERROR "The libcrypt library is required.")
else (NOT CRYPT)
pkg_search_module(CRYPT_M QUIET libcrypt)
if (DEFINED ${CRYPT_M_VERSION} AND ${CRYPT_M_VERSION} VERSION_GREATER "3.1.1")
message (STATUS "\t Using external crypt_gensal_r of ... ${CRYPT_M_VERSION}")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DEXTERNAL_CRYPT_GENSALT_R=1")
endif()
set (CRYPT_LDFLAGS "-lcrypt")
endif (NOT CRYPT)

message (STATUS "Looking for libgcrypt...")
find_library (GCRYPT gcrypt)
message (STATUS "Looking for libgcrypt... ${GCRYPT}")
Expand Down Expand Up @@ -140,11 +155,11 @@ endif()
include_directories (${GLIB_INCLUDE_DIRS} ${GPGME_INCLUDE_DIRS} ${GCRYPT_INCLUDE_DIRS}
${LIBXML2_INCLUDE_DIRS})

set (FILES authutils.c compressutils.c fileutils.c gpgmeutils.c kb.c ldaputils.c
set (FILES passwordbasedauthentication.c compressutils.c fileutils.c gpgmeutils.c kb.c ldaputils.c
nvticache.c radiusutils.c serverutils.c sshutils.c uuidutils.c
xmlutils.c)

set (HEADERS authutils.h compressutils.h fileutils.h gpgmeutils.h kb.h
set (HEADERS passwordbasedauthentication.h authutils.h compressutils.h fileutils.h gpgmeutils.h kb.h
ldaputils.h nvticache.h radiusutils.h serverutils.h sshutils.h
uuidutils.h xmlutils.h)

Expand All @@ -168,13 +183,30 @@ if (BUILD_SHARED)
${RADIUS_LDFLAGS} ${LIBSSH_LDFLAGS} ${GNUTLS_LDFLAGS}
${GCRYPT_LDFLAGS} ${LDAP_LDFLAGS} ${REDIS_LDFLAGS}
${LIBXML2_LDFLAGS} ${UUID_LDFLAGS}
${LINKER_HARDENING_FLAGS})
${LINKER_HARDENING_FLAGS} ${CRYPT_LDFLAGS})
endif (BUILD_SHARED)


## Tests

if (BUILD_TESTS)
add_executable (passwordbasedauthentication-test
EXCLUDE_FROM_ALL
passwordbasedauthentication_tests.c)

add_test (passwordbasedauthentication-test passwordbasedauthentication-test)

target_include_directories (passwordbasedauthentication-test PRIVATE ${CGREEN_INCLUDE_DIRS})

target_link_libraries (passwordbasedauthentication-test ${CGREEN_LIBRARIES}
${BSD_LDFLAGS}
${GCRYPT_LDFLAGS}
${CRYPT_LDFLAGS}
${GLIB_LDFLAGS})

add_custom_target (tests-passwordbasedauthentication
DEPENDS passwordbasedauthentication-test)

add_executable (xmlutils-test
EXCLUDE_FROM_ALL
xmlutils_tests.c)
Expand Down
270 changes: 270 additions & 0 deletions util/passwordbasedauthentication.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
/* Copyright (C) 2020-2021 Greenbone Networks GmbH
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "passwordbasedauthentication.h"
// internal usage to have access to gvm_auth initialized to verify if
// initialization is needed
#include "authutils.c"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// UFC_crypt defines crypt_r when only when __USE_GNU is set
// this shouldn't affect other implementations
#define __USE_GNU
#include <crypt.h>
#ifndef CRYPT_GENSALT_OUTPUT_SIZE
#define CRYPT_GENSALT_OUTPUT_SIZE 192
#endif

#ifndef CRYPT_OUTPUT_SIZE
#define CRYPT_OUTPUT_SIZE 384
#endif

int
is_prefix_supported (const char *id)
{
return strcmp (PREFIX_DEFAULT, id) == 0;
}

// we assume something else than libxcrypt > 3.1; like UFC-crypt
// libxcrypt sets a macro of crypt_gensalt_r to crypt_gensalt_rn
// therefore we could use that mechanism to figure out if we are on
// debian buster or newer.
#ifndef EXTERNAL_CRYPT_GENSALT_R

// used printables within salt
const char ascii64[64] =
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

/* Tries to get BUFLEN random bytes into BUF; returns 0 on success. */
int
get_random (char *buf, size_t buflen)
{
FILE *fp = fopen ("/dev/urandom", "r");
int result = 0;
if (fp == NULL)
{
result = -1;
goto exit;
}
size_t nread = fread (buf, 1, buflen, fp);
fclose (fp);
if (nread < buflen)
{
result = -2;
}

exit:
return result;
}
/* Generate a string suitable for use as the setting when hashing a passphrase.
* PREFIX controls which hash function will be used,
* COUNT controls the computional cost of the hash,
* RBYTES should point to NRBYTES bytes of random data.
*
* If PREFIX is a NULL pointer, the current best default is used; if RBYTES
* is a NULL pointer, random data will be retrieved from the operating system
* if possible.
*
* Teh generated setting string is written to OUTPUT, which is OUTPUT_SIZE long.
* OUTPUT_SIZE must be at least CRYPT_GENSALT_OUTPUT_SIZE.
*
* */
char *
crypt_gensalt_r (const char *prefix, unsigned long count, const char *rbytes,
int nrbytes, char *output, int output_size)
{
char *internal_rbytes = NULL;
unsigned int written = 0, used = 0;
unsigned long value = 0;
if ((rbytes != NULL && nrbytes < 3) || output_size < 16
|| !is_prefix_supported (prefix))
{
output[0] = '*';
goto exit;
}
if (rbytes == NULL)
{
internal_rbytes = malloc (16);
if (get_random (internal_rbytes, 16) != 0)
{
output[0] = '*';
goto exit;
}
nrbytes = 16;
rbytes = internal_rbytes;
}
written = snprintf (output, output_size, "%srounds=%lu$",
prefix == NULL ? PREFIX_DEFAULT : prefix, count);
while (written + 5 < (unsigned int) output_size
&& used + 3 < (unsigned int) nrbytes && (used * 4 / 3) < 16)
{
value = ((unsigned long) rbytes[used + 0] << 0)
| ((unsigned long) rbytes[used + 1] << 8)
| ((unsigned long) rbytes[used + 2] << 16);
output[written] = ascii64[value & 0x3f];
output[written + 1] = ascii64[(value >> 6) & 0x3f];
output[written + 2] = ascii64[(value >> 12) & 0x3f];
output[written + 3] = ascii64[(value >> 18) & 0x3f];
written += 4;
used += 3;
}
output[written] = '\0';
exit:
if (internal_rbytes != NULL)
free (internal_rbytes);
return output[0] == '*' ? 0 : output;
}

#endif

struct PBASettings *
pba_init (const char *pepper, unsigned int pepper_size, unsigned int count,
char *prefix)
{
unsigned int i = 0;
struct PBASettings *result = NULL;
if (pepper_size > MAX_PEPPER_SIZE)
goto exit;
if (prefix != NULL && !is_prefix_supported (prefix))
goto exit;
result = malloc (sizeof (struct PBASettings));
for (i = 0; i < MAX_PEPPER_SIZE; i++)
result->pepper[i] = pepper != NULL && i < pepper_size ? pepper[i] : 0;
result->count = count == 0 ? COUNT_DEFAULT : count;
result->prefix = prefix == NULL ? PREFIX_DEFAULT : prefix;
exit:
return result;
}

void
pba_finalize (struct PBASettings *settings)
{
free (settings);
}

int
pba_is_phc_compliant (const char *setting)
{
if (setting == NULL)
{
return 0;
}
return strlen (setting) > 1 && setting[0] == '$';
}

char *
pba_hash (struct PBASettings *setting, const char *password)
{
char *result = NULL, *settings = NULL, *tmp, *rslt;
int i;
struct crypt_data *data = NULL;

if (!setting || !password)
goto exit;
if (!is_prefix_supported (setting->prefix))
goto exit;
settings = malloc (CRYPT_GENSALT_OUTPUT_SIZE);
if (crypt_gensalt_r (setting->prefix, setting->count, NULL, 0, settings,
CRYPT_GENSALT_OUTPUT_SIZE)
== NULL)
goto exit;
tmp = settings + strlen (settings) - 1;
for (i = MAX_PEPPER_SIZE - 1; i > -1; i--)
{
if (setting->pepper[i] != 0)
tmp[0] = setting->pepper[i];
tmp--;
}

data = calloc (1, sizeof (struct crypt_data));
rslt = crypt_r (password, settings, data);
if (rslt == NULL)
goto exit;
result = malloc (CRYPT_OUTPUT_SIZE);
strncpy(result, rslt, CRYPT_OUTPUT_SIZE);
// remove pepper, by jumping to begin of applied pepper within result
// and overridding it.
tmp = result + (tmp - settings);
for (i = 0; i < MAX_PEPPER_SIZE; i++)
{
tmp++;
if (setting->pepper[i] != 0)
tmp[0] = '0';
}
exit:
if (data != NULL)
free (data);
if (settings != NULL)
free (settings);
return result;
}

enum pba_rc
pba_verify_hash (const struct PBASettings *setting, const char *hash,
const char *password)
{
char *cmp, *tmp = NULL;
struct crypt_data *data = NULL;
int i = 0;
enum pba_rc result = ERR;
if (!setting || !hash || !password)
goto exit;
if (!is_prefix_supported (setting->prefix))
goto exit;
if (pba_is_phc_compliant (hash) != 0)
{
data = calloc (1, sizeof (struct crypt_data));
// manipulate hash to reapply pepper
tmp = malloc ( CRYPT_OUTPUT_SIZE);
strncpy(tmp, hash, CRYPT_OUTPUT_SIZE);
cmp = strrchr (tmp, '$');
for (i = MAX_PEPPER_SIZE - 1; i > -1; i--)
{
cmp--;
if (setting->pepper[i] != 0)
cmp[0] = setting->pepper[i];
}
cmp = crypt_r (password, tmp, data);
if (strcmp (tmp, cmp) == 0)
result = VALID;
else
result = INVALID;
}
else
{
// assume authutils hash handling
// initialize gvm_auth utils if not already initialized
if (initialized == FALSE && gvm_auth_init () != 0)
{
goto exit;
}
// verify result of gvm_authenticate_classic
i = gvm_authenticate_classic (NULL, password, hash);
if (i == 0)
result = UPDATE_RECOMMENDED;
else if (i == 1)
result = INVALID;
}
exit:
if (data != NULL)
free (data);
if (tmp != NULL)
free (tmp);
return result;
}
Loading

0 comments on commit a62c406

Please sign in to comment.