Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introducing passwordbasedauthentication based on lcrypt and sha512 (backport #484) #487

Merged
merged 13 commits into from
Apr 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
build/
.ccls
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [21.04.1] (unreleased)

### Added
- Possibility to use lcrypt with `$6$` (sha512) for authentication [484](https://github.com/greenbone/gvm-libs/pull/484)

### Changed
### Fixed
- Unify GLib log domains [#479](https://github.com/greenbone/gvm-libs/pull/479)
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