passwordbasedauthentication.c
Go to the documentation of this file.
/* SPDX-FileCopyrightText: 2020-2023 Greenbone AG
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#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>
// INVALID_HASH is used on verify when the given hash is a NULL pointer.
// This is done to not directly jump to exit with a INVALID_HASH result
// but rather keep calculating to make it a little bit harder to guess
// if a user exists or not based on timing.
#define INVALID_HASH "1234567890$"
#ifndef CRYPT_GENSALT_OUTPUT_SIZE
#define CRYPT_GENSALT_OUTPUT_SIZE 192
#endif
#ifndef CRYPT_OUTPUT_SIZE
#define CRYPT_OUTPUT_SIZE 384
#endif
static 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[] =
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static 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;
}
char *
crypt_gensalt_r (const char *prefix, unsigned long count, const char *rbytes,
int nrbytes, char *output, int 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);
}
static int
pba_is_phc_compliant (const char *setting)
{
if (setting == NULL)
{
return 1;
}
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 = calloc (1, CRYPT_OUTPUT_SIZE);
memcpy (result, rslt, CRYPT_OUTPUT_SIZE);
// remove pepper, by jumping to begin of applied pepper within result
// and overriding 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;
char *invalid_hash = calloc (1, CRYPT_OUTPUT_SIZE);
memset (invalid_hash, 0, CRYPT_OUTPUT_SIZE);
memcpy (invalid_hash, INVALID_HASH, strlen (INVALID_HASH));
if (!setting)
goto exit;
if (!is_prefix_supported (setting->prefix))
goto exit;
if (pba_is_phc_compliant (hash) != 0)
{
int hash_size;
hash_size = hash ? strlen (hash) : strlen (invalid_hash);
data = calloc (1, sizeof (struct crypt_data));
// manipulate hash to reapply pepper
tmp = calloc (1, CRYPT_OUTPUT_SIZE);
memset (tmp, 0, CRYPT_OUTPUT_SIZE);
memcpy (tmp, hash ? hash : invalid_hash,
(hash_size < CRYPT_OUTPUT_SIZE) ? hash_size
: CRYPT_OUTPUT_SIZE - 1);
cmp = strrchr (tmp, '$');
for (i = MAX_PEPPER_SIZE - 1; i > -1; i--)
{
cmp--;
if (setting->pepper[i] != 0)
cmp[0] = setting->pepper[i];
}
// some crypt_r implementations cannot handle if password is a
// NULL pointer and run into SEGMENTATION faults.
// Therefore we set it to ""
cmp = crypt_r (password ? 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:
free (invalid_hash);
if (data != NULL)
free (data);
if (tmp != NULL)
free (tmp);
return result;
}
authutils.c
Authentication mechanism(s).
gvm_authenticate_classic
int gvm_authenticate_classic(const gchar *username, const gchar *password, const gchar *hash_arg)
Authenticate a credential pair against user file contents.
Definition authutils.c:274
gvm_auth_init
int gvm_auth_init(void)
Initializes Gcrypt.
Definition authutils.c:109
initialized
static gboolean initialized
Flag whether the config file was read.
Definition authutils.c:33
pba_is_phc_compliant
static int pba_is_phc_compliant(const char *setting)
Check if a PBA settings is PHC compliant.
Definition passwordbasedauthentication.c:202
get_random
static int get_random(char *buf, size_t buflen)
Try to get random bytes.
Definition passwordbasedauthentication.c:63
INVALID_HASH
#define INVALID_HASH
Definition passwordbasedauthentication.c:22
ascii64
const char ascii64[]
Definition passwordbasedauthentication.c:51
crypt_gensalt_r
char * crypt_gensalt_r(const char *prefix, unsigned long count, const char *rbytes, int nrbytes, char *output, int output_size)
Generate string suitable for use as setting when hashing a passphrase.
Definition passwordbasedauthentication.c:104
pba_finalize
void pba_finalize(struct PBASettings *settings)
Cleanup PBA settings.
Definition passwordbasedauthentication.c:189
CRYPT_OUTPUT_SIZE
#define CRYPT_OUTPUT_SIZE
Definition passwordbasedauthentication.c:28
pba_hash
char * pba_hash(struct PBASettings *setting, const char *password)
Create a password hash.
Definition passwordbasedauthentication.c:220
pba_init
struct PBASettings * pba_init(const char *pepper, unsigned int pepper_size, unsigned int count, char *prefix)
Init PBA.
Definition passwordbasedauthentication.c:165
pba_verify_hash
enum pba_rc pba_verify_hash(const struct PBASettings *setting, const char *hash, const char *password)
Verify a password hash.
Definition passwordbasedauthentication.c:276
CRYPT_GENSALT_OUTPUT_SIZE
#define CRYPT_GENSALT_OUTPUT_SIZE
Definition passwordbasedauthentication.c:24
is_prefix_supported
static int is_prefix_supported(const char *id)
Check if a prefix is supported.
Definition passwordbasedauthentication.c:39
passwordbasedauthentication.h
COUNT_DEFAULT
#define COUNT_DEFAULT
Definition passwordbasedauthentication.h:12
pba_rc
pba_rc
Definition passwordbasedauthentication.h:46
ERR
@ ERR
Definition passwordbasedauthentication.h:50
VALID
@ VALID
Definition passwordbasedauthentication.h:47
UPDATE_RECOMMENDED
@ UPDATE_RECOMMENDED
Definition passwordbasedauthentication.h:48
INVALID
@ INVALID
Definition passwordbasedauthentication.h:49
MAX_PEPPER_SIZE
#define MAX_PEPPER_SIZE
Definition passwordbasedauthentication.h:10
PREFIX_DEFAULT
#define PREFIX_DEFAULT
Definition passwordbasedauthentication.h:14
PBASettings
Definition passwordbasedauthentication.h:23
PBASettings::pepper
char pepper[MAX_PEPPER_SIZE]
Definition passwordbasedauthentication.h:24
PBASettings::prefix
char * prefix
Definition passwordbasedauthentication.h:26
PBASettings::count
unsigned int count
Definition passwordbasedauthentication.h:25