mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-06-25 12:16:40 +02:00
620 lines
17 KiB
C
620 lines
17 KiB
C
/*
|
||
* Unreal Internet Relay Chat Daemon, src/auth.c
|
||
* (C) 2001 Carsten V. Munk (stskeeps@tspre.org)
|
||
* (C) 2003-2019 Bram Matthys (syzop@vulnscan.org) and the UnrealIRCd team
|
||
*
|
||
* 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 1, or (at your option)
|
||
* any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this program; if not, write to the Free Software
|
||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
*/
|
||
|
||
#include "unrealircd.h"
|
||
#include "crypt_blowfish.h"
|
||
|
||
typedef struct AuthTypeList AuthTypeList;
|
||
struct AuthTypeList {
|
||
char *name;
|
||
AuthenticationType type;
|
||
};
|
||
|
||
/** The list of authentication types that we support. */
|
||
AuthTypeList MODVAR AuthTypeLists[] = {
|
||
{"plain", AUTHTYPE_PLAINTEXT},
|
||
{"plaintext", AUTHTYPE_PLAINTEXT},
|
||
{"crypt", AUTHTYPE_UNIXCRYPT},
|
||
{"unixcrypt", AUTHTYPE_UNIXCRYPT},
|
||
{"bcrypt", AUTHTYPE_BCRYPT},
|
||
{"cert", AUTHTYPE_TLS_CLIENTCERT},
|
||
{"sslclientcert", AUTHTYPE_TLS_CLIENTCERT},
|
||
{"tlsclientcert", AUTHTYPE_TLS_CLIENTCERT},
|
||
{"certfp", AUTHTYPE_TLS_CLIENTCERTFP},
|
||
{"sslclientcertfp", AUTHTYPE_TLS_CLIENTCERTFP},
|
||
{"tlsclientcertfp", AUTHTYPE_TLS_CLIENTCERTFP},
|
||
{"spkifp", AUTHTYPE_SPKIFP},
|
||
{"argon2", AUTHTYPE_ARGON2},
|
||
{NULL, 0}
|
||
};
|
||
|
||
/* Forward declarations */
|
||
static char *mkpass_argon2(const char *para);
|
||
|
||
/* Helper function for Auth_AutoDetectHashType() */
|
||
static int parsepass(const char *str, char **salt, char **hash)
|
||
{
|
||
static char saltbuf[512], hashbuf[512];
|
||
const char *p;
|
||
int max;
|
||
|
||
/* Syntax: $<salt>$<hash> */
|
||
if (*str != '$')
|
||
return 0;
|
||
p = strchr(str+1, '$');
|
||
if (!p || (p == str+1) || !p[1])
|
||
return 0;
|
||
|
||
max = p - str;
|
||
if (max > sizeof(saltbuf))
|
||
max = sizeof(saltbuf);
|
||
strlcpy(saltbuf, str+1, max);
|
||
strlcpy(hashbuf, p+1, sizeof(hashbuf));
|
||
*salt = saltbuf;
|
||
*hash = hashbuf;
|
||
return 1;
|
||
}
|
||
|
||
/** Auto detect hash type for input hash 'hash'.
|
||
* Will fallback to AUTHTYPE_PLAINTEXT when not found (or invalid).
|
||
*/
|
||
int Auth_AutoDetectHashType(const char *hash)
|
||
{
|
||
static char hashbuf[256];
|
||
char *saltstr, *hashstr;
|
||
int bits;
|
||
|
||
if (!strchr(hash, '$'))
|
||
{
|
||
/* SHA256 certificate fingerprint perhaps?
|
||
* These are exactly 64 bytes (00112233..etc..) or 95 bytes (00:11:22:33:etc) in size.
|
||
*/
|
||
if ((strlen(hash) == 64) || (strlen(hash) == 95))
|
||
{
|
||
const char *p;
|
||
char *hexchars = "0123456789abcdefABCDEF";
|
||
for (p = hash; *p; p++)
|
||
if ((*p != ':') && !strchr(hexchars, *p))
|
||
return AUTHTYPE_PLAINTEXT; /* not hex and not colon */
|
||
|
||
return AUTHTYPE_TLS_CLIENTCERTFP;
|
||
}
|
||
|
||
if (strlen(hash) == 44)
|
||
{
|
||
const char *p;
|
||
char *b64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||
for (p = hash; *p; p++)
|
||
if (!strchr(b64chars, *p))
|
||
return AUTHTYPE_PLAINTEXT; /* not base64 */
|
||
|
||
return AUTHTYPE_SPKIFP;
|
||
}
|
||
}
|
||
|
||
/* gcc UBSan bug in at least gcc 12.2.0, 14.2.0 and 15.2.0.
|
||
* The second part of the if is only evaluated if *hash == '$'
|
||
* and thus hash+1 is at least \0, but gcc thinks otherwise.
|
||
* auth.c:112:32: error: ‘strchr’ reading 1 or more bytes from a region of size 0 [-Werror=stringop-overread]
|
||
112 | if ((*hash != '$') || !strchr(hash+1, '$'))
|
||
*/
|
||
#if defined(__GNUC__)
|
||
#pragma GCC diagnostic push
|
||
#pragma GCC diagnostic ignored "-Wstringop-overread"
|
||
#endif
|
||
if ((*hash != '$') || !strchr(hash+1, '$'))
|
||
return AUTHTYPE_PLAINTEXT;
|
||
#if defined(__GNUC__)
|
||
#pragma GCC diagnostic pop
|
||
#endif
|
||
|
||
if (!strncmp(hash, "$2a$", 4) || !strncmp(hash, "$2b$", 4) || !strncmp(hash, "$2y$", 4))
|
||
return AUTHTYPE_BCRYPT;
|
||
|
||
if (!strncmp(hash, "$argon2", 7))
|
||
return AUTHTYPE_ARGON2;
|
||
|
||
/* Now handle UnrealIRCd-style password hashes.. */
|
||
if (parsepass(hash, &saltstr, &hashstr) == 0)
|
||
return AUTHTYPE_PLAINTEXT; /* old method (pre-3.2.1) or could not detect, fallback. */
|
||
|
||
bits = b64_decode(hashstr, hashbuf, sizeof(hashbuf)) * 8;
|
||
if (bits <= 0)
|
||
return AUTHTYPE_UNIXCRYPT; /* decode failed. likely some other crypt() type. */
|
||
|
||
/* else it's likely some other crypt() type */
|
||
return AUTHTYPE_UNIXCRYPT;
|
||
}
|
||
|
||
|
||
/** Find authentication type for 'hash' and explicit type 'type'.
|
||
* @param hash The password hash (may be NULL if you are creating a password)
|
||
* @param type An explicit type. In that case we will search by this type, rather
|
||
* than trying to determine the type on the 'hash' parameter.
|
||
* Or leave NULL, then we use hash autodetection.
|
||
*/
|
||
AuthenticationType Auth_FindType(const char *hash, const char *type)
|
||
{
|
||
if (type)
|
||
{
|
||
AuthTypeList *e = AuthTypeLists;
|
||
while (e->name)
|
||
{
|
||
if (!mycmp(e->name, type))
|
||
return e->type;
|
||
e++;
|
||
}
|
||
return AUTHTYPE_INVALID; /* Not found */
|
||
}
|
||
|
||
if (hash)
|
||
return Auth_AutoDetectHashType(hash);
|
||
|
||
return AUTHTYPE_INVALID; /* both 'hash' and 'type' are NULL */
|
||
}
|
||
|
||
/** Check the syntax of an authentication block.
|
||
* This is a block like: password "data" { type; };
|
||
* in the configuration file.
|
||
*/
|
||
int Auth_CheckError(ConfigEntry *ce, int warn_on_plaintext)
|
||
{
|
||
AuthenticationType type = AUTHTYPE_PLAINTEXT;
|
||
X509 *x509_filecert = NULL;
|
||
FILE *x509_f = NULL;
|
||
if (!ce->value)
|
||
{
|
||
config_error("%s:%i: authentication module failure: missing parameter",
|
||
ce->file->filename, ce->line_number);
|
||
return -1;
|
||
}
|
||
if (ce->items && ce->items->next)
|
||
{
|
||
config_error("%s:%i: you may not have multiple authentication methods",
|
||
ce->file->filename, ce->line_number);
|
||
return -1;
|
||
}
|
||
|
||
type = Auth_FindType(ce->value, ce->items ? ce->items->name : NULL);
|
||
if (type == -1)
|
||
{
|
||
config_error("%s:%i: authentication module failure: %s is not an implemented/enabled authentication method",
|
||
ce->file->filename, ce->line_number,
|
||
ce->items->name);
|
||
return -1;
|
||
}
|
||
|
||
switch (type)
|
||
{
|
||
case AUTHTYPE_PLAINTEXT:
|
||
if (warn_on_plaintext && bestpractices.hashed_passwords)
|
||
{
|
||
const char *hashedpass = mkpass_argon2(ce->value);
|
||
unreal_log(ULOG_ADVICE, "config", "BEST_PRACTICES_HASHED_PASSWORDS", NULL,
|
||
"$file:$line_number: $config_item: Advice: it is not recommended to use plaintext passwords in the config file. "
|
||
"You can replace this password with the following password hash:\n"
|
||
"password \"$hashed_password\";",
|
||
log_data_string("config_item", config_item_name(ce)),
|
||
log_data_string("file", ce->file->filename),
|
||
log_data_integer("line_number", ce->line_number),
|
||
log_data_string("hashed_password", hashedpass));
|
||
bestpractices.hashed_passwords_hits++;
|
||
}
|
||
break;
|
||
case AUTHTYPE_UNIXCRYPT:
|
||
/* If our data is like 1 or none, we just let em through .. */
|
||
if (strlen(ce->value) < 2)
|
||
{
|
||
config_error("%s:%i: authentication module failure: AUTHTYPE_UNIXCRYPT: no salt (crypt strings will always be >2 in length)",
|
||
ce->file->filename, ce->line_number);
|
||
return -1;
|
||
}
|
||
break;
|
||
case AUTHTYPE_TLS_CLIENTCERT:
|
||
convert_to_absolute_path(&ce->value, CONFDIR);
|
||
if (!(x509_f = fopen(ce->value, "r")))
|
||
{
|
||
config_error("%s:%i: authentication module failure: AUTHTYPE_TLS_CLIENTCERT: error opening file %s: %s",
|
||
ce->file->filename, ce->line_number, ce->value, strerror(errno));
|
||
return -1;
|
||
}
|
||
x509_filecert = PEM_read_X509(x509_f, NULL, NULL, NULL);
|
||
fclose(x509_f);
|
||
if (!x509_filecert)
|
||
{
|
||
config_error("%s:%i: authentication module failure: AUTHTYPE_TLS_CLIENTCERT: PEM_read_X509 errored in file %s (format error?)",
|
||
ce->file->filename, ce->line_number, ce->value);
|
||
return -1;
|
||
}
|
||
X509_free(x509_filecert);
|
||
break;
|
||
default: ;
|
||
}
|
||
|
||
/* Unix crypt is a bit more complicated: most types are outright 'bad',
|
||
* while other types have reasonable security similar to 'bcrypt'.
|
||
* To be honest these people should probably use 'argon2' since it's
|
||
* a lot better. Then again, warning about this when it's still such
|
||
* a common hashing method (now, in 2018) may be a bit overzealous.
|
||
* So: not warning about crypt types $5/$6 which use SHA256/SHA512
|
||
* with normally at least 5000 rounds (unless deliberately weakened
|
||
* by the user).
|
||
*/
|
||
if ((type == AUTHTYPE_UNIXCRYPT) && strncmp(ce->value, "$5", 2) &&
|
||
strncmp(ce->value, "$6", 2) && !strstr(ce->value, "$rounds"))
|
||
{
|
||
config_warn("%s:%i: Using simple crypt for authentication is not recommended. "
|
||
"Consider using the more secure auth-type 'argon2' instead. "
|
||
"See https://www.unrealircd.org/docs/Authentication_types for the complete list.",
|
||
ce->file->filename, ce->line_number);
|
||
/* do not return, not an error. */
|
||
}
|
||
if ((type == AUTHTYPE_PLAINTEXT) && (strlen(ce->value) > PASSWDLEN))
|
||
{
|
||
config_error("%s:%i: passwords length may not exceed %d",
|
||
ce->file->filename, ce->line_number, PASSWDLEN);
|
||
return -1;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/** Convert an authentication block from the configuration file
|
||
* into an AuthConfig structure so it can be used at runtime.
|
||
*/
|
||
void AuthBlockToAuthConfig(ConfigEntry *ce, AuthConfig **list)
|
||
{
|
||
AuthenticationType type = AUTHTYPE_PLAINTEXT;
|
||
AuthConfig *as = NULL;
|
||
|
||
type = Auth_FindType(ce->value, ce->items ? ce->items->name : NULL);
|
||
if (type == AUTHTYPE_INVALID)
|
||
type = AUTHTYPE_PLAINTEXT;
|
||
|
||
as = safe_alloc(sizeof(AuthConfig));
|
||
safe_strdup(as->data, ce->value);
|
||
as->type = type;
|
||
|
||
AddListItem(as, *list);
|
||
}
|
||
|
||
/** Free an AuthConfig struct */
|
||
void Auth_FreeAuthConfig(AuthConfig *as)
|
||
{
|
||
AuthConfig *as_next;
|
||
for (; as; as = as_next)
|
||
{
|
||
as_next = as->next;
|
||
safe_free(as->data);
|
||
safe_free(as);
|
||
}
|
||
}
|
||
|
||
/* RAW salt length (before b64_encode) to use in /MKPASSWD
|
||
* and REAL salt length (after b64_encode, including terminating nul),
|
||
* used for reserving memory.
|
||
*/
|
||
#define RAWSALTLEN 6
|
||
#define REALSALTLEN 12
|
||
|
||
static int authcheck_argon2(Client *client, AuthConfig *as, const char *para)
|
||
{
|
||
argon2_type hashtype;
|
||
|
||
if (!para)
|
||
return 0;
|
||
|
||
/* Find out the hashtype. Why do we need to do this, why is this
|
||
* not in the library or irrelevant by using some generic function?
|
||
*/
|
||
if (!strncmp(as->data, "$argon2id", 9))
|
||
hashtype = Argon2_id;
|
||
else if (!strncmp(as->data, "$argon2i", 8))
|
||
hashtype = Argon2_i;
|
||
else if (!strncmp(as->data, "$argon2d", 8))
|
||
hashtype = Argon2_d;
|
||
else
|
||
return 0; /* unknown argon2 type */
|
||
|
||
if (argon2_verify(as->data, para, strlen(para), hashtype) == ARGON2_OK)
|
||
return 1; /* MATCH */
|
||
|
||
return 0; /* NO MATCH or error */
|
||
}
|
||
|
||
static int authcheck_bcrypt(Client *client, AuthConfig *as, const char *para)
|
||
{
|
||
char data[512]; /* NOTE: only 64 required by BF_crypt() */
|
||
char *str;
|
||
|
||
if (!para)
|
||
return 0;
|
||
|
||
memset(data, 0, sizeof(data));
|
||
str = _crypt_blowfish_rn(para, as->data, data, sizeof(data));
|
||
|
||
if (!str)
|
||
return 0; /* ERROR / INVALID HASH */
|
||
|
||
if (!strcmp(str, as->data))
|
||
return 1; /* MATCH */
|
||
|
||
return 0; /* NO MATCH */
|
||
}
|
||
|
||
static int authcheck_tls_clientcert(Client *client, AuthConfig *as, const char *para)
|
||
{
|
||
X509 *x509_clientcert = NULL;
|
||
X509 *x509_filecert = NULL;
|
||
FILE *x509_f = NULL;
|
||
|
||
if (!client->local->ssl)
|
||
return 0;
|
||
x509_clientcert = SSL_get_peer_certificate(client->local->ssl);
|
||
if (!x509_clientcert)
|
||
return 0;
|
||
if (!(x509_f = fopen(as->data, "r")))
|
||
{
|
||
X509_free(x509_clientcert);
|
||
return 0;
|
||
}
|
||
x509_filecert = PEM_read_X509(x509_f, NULL, NULL, NULL);
|
||
fclose(x509_f);
|
||
if (!x509_filecert)
|
||
{
|
||
X509_free(x509_clientcert);
|
||
return 0;
|
||
}
|
||
if (X509_cmp(x509_filecert, x509_clientcert) != 0)
|
||
{
|
||
X509_free(x509_clientcert);
|
||
X509_free(x509_filecert);
|
||
return 0;
|
||
}
|
||
X509_free(x509_clientcert);
|
||
X509_free(x509_filecert);
|
||
return 1;
|
||
}
|
||
|
||
static int authcheck_tls_clientcert_fingerprint(Client *client, AuthConfig *as, const char *para)
|
||
{
|
||
int i, k;
|
||
char hexcolon[EVP_MAX_MD_SIZE * 3 + 1];
|
||
const char *fp;
|
||
|
||
if (!client->local->ssl)
|
||
return 0;
|
||
|
||
fp = moddata_client_get(client, "certfp");
|
||
if (!fp)
|
||
return 0;
|
||
|
||
if (!strcmp(as->data, fp))
|
||
return 1;
|
||
|
||
/* Check colon version, so that we keep in line with
|
||
* previous versions, based on Nath's patch -dboyz
|
||
*/
|
||
for (i=0, k=0; i<strlen(fp); i++)
|
||
{
|
||
if (i != 0 && i % 2 == 0)
|
||
hexcolon[k++] = ':';
|
||
hexcolon[k++] = fp[i];
|
||
}
|
||
hexcolon[k] = '\0';
|
||
|
||
if (!strcasecmp(as->data, hexcolon))
|
||
return 1;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int authcheck_spkifp(Client *client, AuthConfig *as, const char *para)
|
||
{
|
||
const char *fp = spki_fingerprint(client);
|
||
|
||
if (!fp)
|
||
return 0; /* auth failed: not TLS or some other failure */
|
||
|
||
if (strcasecmp(as->data, fp))
|
||
return 0; /* auth failed: mismatch */
|
||
|
||
return 1; /* SUCCESS */
|
||
}
|
||
|
||
|
||
/*
|
||
* client MUST be a local client
|
||
* as is what it will be compared with
|
||
* para will used in coordination with the auth type
|
||
*/
|
||
|
||
/** Check authentication, such as a password against the
|
||
* provided AuthConfig (which was parsed from the configuration
|
||
* file earlier).
|
||
* @param client The client.
|
||
* @param as The authentication config.
|
||
* @param para The provided parameter (NULL allowed)
|
||
* @returns 1 if passed, 0 if incorrect (eg: invalid password)
|
||
* @note
|
||
* - The return value was different in versions before UnrealIRCd 5.0.0!
|
||
* - In older versions a NULL 'as' was treated as an allow, now it's deny.
|
||
*/
|
||
int Auth_Check(Client *client, AuthConfig *as, const char *para)
|
||
{
|
||
extern char *crypt();
|
||
char *res;
|
||
|
||
if (!as || !as->data)
|
||
return 0; /* Should not happen, but better be safe.. */
|
||
|
||
for (; as; as = as->next)
|
||
{
|
||
switch (as->type)
|
||
{
|
||
case AUTHTYPE_PLAINTEXT:
|
||
if (!para)
|
||
return 0;
|
||
if (!strcmp(as->data, "changemeplease") && !strcmp(para, as->data))
|
||
{
|
||
unreal_log(ULOG_INFO, "auth", "AUTH_REJECT_DEFAULT_PASSWORD", client,
|
||
"Rejecting default password 'changemeplease'. "
|
||
"Please change the password in the configuration file.");
|
||
return 0;
|
||
}
|
||
/* plain text compare */
|
||
if (!strcmp(para, as->data))
|
||
return 1;
|
||
break;
|
||
|
||
case AUTHTYPE_ARGON2:
|
||
if (authcheck_argon2(client, as, para))
|
||
return 1;
|
||
break;
|
||
|
||
case AUTHTYPE_BCRYPT:
|
||
if (authcheck_bcrypt(client, as, para))
|
||
return 1;
|
||
break;
|
||
|
||
case AUTHTYPE_UNIXCRYPT:
|
||
if (!para)
|
||
return 0;
|
||
res = crypt(para, as->data);
|
||
if (res && !strcmp(res, as->data))
|
||
return 1;
|
||
break;
|
||
|
||
case AUTHTYPE_TLS_CLIENTCERT:
|
||
if (authcheck_tls_clientcert(client, as, para))
|
||
return 1;
|
||
break;
|
||
|
||
case AUTHTYPE_TLS_CLIENTCERTFP:
|
||
if (authcheck_tls_clientcert_fingerprint(client, as, para))
|
||
return 1;
|
||
break;
|
||
|
||
case AUTHTYPE_SPKIFP:
|
||
if (authcheck_spkifp(client, as, para))
|
||
return 1;
|
||
break;
|
||
|
||
case AUTHTYPE_INVALID:
|
||
#ifdef DEBUGMODE
|
||
abort();
|
||
#endif
|
||
break; /* Should never happen */
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
#define UNREALIRCD_ARGON2_DEFAULT_TIME_COST 2
|
||
#define UNREALIRCD_ARGON2_DEFAULT_MEMORY_COST 6144
|
||
#define UNREALIRCD_ARGON2_DEFAULT_PARALLELISM_COST 2
|
||
#define UNREALIRCD_ARGON2_DEFAULT_HASH_LENGTH 32
|
||
#define UNREALIRCD_ARGON2_DEFAULT_SALT_LENGTH (128/8)
|
||
|
||
static char *mkpass_argon2(const char *para)
|
||
{
|
||
static char buf[512];
|
||
char salt[UNREALIRCD_ARGON2_DEFAULT_SALT_LENGTH];
|
||
int ret, i;
|
||
|
||
if (!para)
|
||
return NULL;
|
||
|
||
/* Initialize salt */
|
||
for (i=0; i < sizeof(salt); i++)
|
||
salt[i] = getrandom8();
|
||
|
||
*buf = '\0';
|
||
|
||
ret = argon2id_hash_encoded(UNREALIRCD_ARGON2_DEFAULT_TIME_COST,
|
||
UNREALIRCD_ARGON2_DEFAULT_MEMORY_COST,
|
||
UNREALIRCD_ARGON2_DEFAULT_PARALLELISM_COST,
|
||
para,
|
||
strlen(para),
|
||
salt,
|
||
sizeof(salt),
|
||
UNREALIRCD_ARGON2_DEFAULT_HASH_LENGTH,
|
||
buf,
|
||
sizeof(buf));
|
||
|
||
if (ret != ARGON2_OK)
|
||
return NULL; /* internal error */
|
||
|
||
return buf;
|
||
}
|
||
|
||
static char *mkpass_bcrypt(const char *para)
|
||
{
|
||
static char buf[128];
|
||
char data[512]; /* NOTE: only 64 required by BF_crypt() */
|
||
char salt[64];
|
||
char random_data[32];
|
||
char *str;
|
||
char *saltstr;
|
||
int i;
|
||
|
||
if (!para)
|
||
return NULL;
|
||
|
||
memset(data, 0, sizeof(data));
|
||
|
||
for (i=0; i<sizeof(random_data); i++)
|
||
random_data[i] = getrandom8();
|
||
|
||
saltstr = _crypt_gensalt_blowfish_rn("$2y", 9, random_data, sizeof(random_data), salt, sizeof(salt));
|
||
if (!saltstr)
|
||
return NULL;
|
||
|
||
str = _crypt_blowfish_rn(para, saltstr, data, sizeof(data));
|
||
|
||
if (!str)
|
||
return NULL; /* INTERNAL ERROR */
|
||
|
||
strlcpy(buf, str, sizeof(buf));
|
||
return buf;
|
||
}
|
||
|
||
/** Create a hashed password for the specified string.
|
||
* @param type One of AUTHTYPE_*, eg AUTHTYPE_ARGON2.
|
||
* @param text The password in plaintext.
|
||
* @returns The hashed password.
|
||
*/
|
||
const char *Auth_Hash(AuthenticationType type, const char *text)
|
||
{
|
||
switch (type)
|
||
{
|
||
case AUTHTYPE_PLAINTEXT:
|
||
return text;
|
||
|
||
case AUTHTYPE_ARGON2:
|
||
return mkpass_argon2(text);
|
||
|
||
case AUTHTYPE_BCRYPT:
|
||
return mkpass_bcrypt(text);
|
||
|
||
default:
|
||
return NULL;
|
||
}
|
||
}
|