mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-07-09 14:03:12 +02:00
Add unrealdb and secrets API. Documentation and more information will
follow in later commits.
This commit is contained in:
+51
@@ -545,9 +545,31 @@ extern void *safe_alloc(size_t size);
|
||||
*/
|
||||
#define raw_strldup(str, max) our_strldup(str, max)
|
||||
|
||||
extern void *safe_alloc_sensitive(size_t size);
|
||||
|
||||
/** Free previously allocate memory pointer - this is the sensitive version which
|
||||
* may ONLY be called on allocations returned by safe_alloc_sensitive() / safe_strdup_sensitive().
|
||||
* This will set the memory to all zeroes before actually deallocating.
|
||||
* It also sets the pointer to NULL, since that would otherwise be common to forget.
|
||||
* @note If you call this function on normally allocated memory (non-sensitive) then we will crash.
|
||||
*/
|
||||
#define safe_free_sensitive(x) do { if (x) sodium_free(x); x = NULL; } while(0)
|
||||
|
||||
/** Free previous memory (if any) and then save a duplicate of the specified string -
|
||||
* This is the 'sensitive' version which should only be used for HIGHLY sensitive data,
|
||||
* as it wastes about 8000 bytes even if you only duplicate a string of 32 bytes (this is by design).
|
||||
* @param dst The current pointer and the pointer where a new copy of the string will be stored.
|
||||
* @param str The string you want to copy
|
||||
*/
|
||||
#define safe_strdup_sensitive(dst,str) do { if (dst) sodium_free(dst); if (!(str)) dst = NULL; else dst = our_strdup_sensitive(str); } while(0)
|
||||
|
||||
/** Safely destroy a string in memory (but do not free!) */
|
||||
#define destroy_string(str) sodium_memzero(str, strlen(str))
|
||||
|
||||
/** @} */
|
||||
extern char *our_strdup(const char *str);
|
||||
extern char *our_strldup(const char *str, size_t max);
|
||||
extern char *our_strdup_sensitive(const char *str);
|
||||
|
||||
extern long config_checkval(char *value, unsigned short flags);
|
||||
extern void config_status(FORMAT_STRING(const char *format), ...) __attribute__((format(printf,1,2)));
|
||||
@@ -1007,3 +1029,32 @@ extern NameValuePrioList *find_nvplist(NameValuePrioList *list, char *name);
|
||||
extern void free_nvplist(NameValuePrioList *lst);
|
||||
extern char *get_connect_extinfo(Client *client);
|
||||
extern char *unreal_strftime(char *str);
|
||||
/* src/unrealdb.c start */
|
||||
extern UnrealDB *unrealdb_open(const char *filename, UnrealDBMode mode, char *secret_block);
|
||||
extern int unrealdb_close(UnrealDB *c);
|
||||
extern char *unrealdb_test_db(const char *filename, char *secret_block);
|
||||
extern int unrealdb_write_int64(UnrealDB *c, uint64_t t);
|
||||
extern int unrealdb_write_int32(UnrealDB *c, uint32_t t);
|
||||
extern int unrealdb_write_int16(UnrealDB *c, uint16_t t);
|
||||
extern int unrealdb_write_str(UnrealDB *c, char *x);
|
||||
extern int unrealdb_write_data(UnrealDB *c, void *buf, int len);
|
||||
extern int unrealdb_read_int64(UnrealDB *c, uint64_t *t);
|
||||
extern int unrealdb_read_int32(UnrealDB *c, uint32_t *t);
|
||||
extern int unrealdb_read_int16(UnrealDB *c, uint16_t *t);
|
||||
extern int unrealdb_read_str(UnrealDB *c, char **x);
|
||||
extern int unrealdb_read_data(UnrealDB *c, void *buf, int len);
|
||||
extern char *unrealdb_test_secret(char *name);
|
||||
extern UnrealDBConfig *unrealdb_copy_config(UnrealDBConfig *src);
|
||||
extern UnrealDBConfig *unrealdb_get_config(UnrealDB *db);
|
||||
extern void unrealdb_free_config(UnrealDBConfig *c);
|
||||
extern UnrealDBError unrealdb_get_error_code(void);
|
||||
extern char *unrealdb_get_error_string(void);
|
||||
/* src/unrealdb.c end */
|
||||
/* secret { } related stuff */
|
||||
extern Secret *find_secret(char *secret_name);
|
||||
extern void free_secret_cache(SecretCache *c);
|
||||
extern void free_secret(Secret *s);
|
||||
extern Secret *secrets;
|
||||
/* end */
|
||||
extern int check_password_strength(char *pass, int min_length, int strict, char **err);
|
||||
extern int valid_secret_password(char *pass, char **err);
|
||||
|
||||
@@ -109,6 +109,7 @@ typedef struct ConfigItem_blacklist_module ConfigItem_blacklist_module;
|
||||
typedef struct ConfigItem_help ConfigItem_help;
|
||||
typedef struct ConfigItem_offchans ConfigItem_offchans;
|
||||
typedef struct SecurityGroup SecurityGroup;
|
||||
typedef struct Secret Secret;
|
||||
typedef struct ListStruct ListStruct;
|
||||
typedef struct ListStructPrio ListStructPrio;
|
||||
|
||||
@@ -862,6 +863,91 @@ typedef void (*CmdFunc)(Client *client, MessageTag *mtags, int parc, char *parv[
|
||||
typedef void (*AliasCmdFunc)(Client *client, MessageTag *mtags, int parc, char *parv[], char *cmd);
|
||||
typedef void (*OverrideCmdFunc)(CommandOverride *ovr, Client *client, MessageTag *mtags, int parc, char *parv[]);
|
||||
|
||||
#include <sodium.h>
|
||||
|
||||
/* This is the 'chunk size', the size of encryption blocks.
|
||||
* We choose 4K here since that is a decent amount as of 2021 and
|
||||
* more would not benefit performance anyway.
|
||||
* Note that you cannot change this value easily afterwards
|
||||
* (you cannot read files with a different chunk size).
|
||||
*/
|
||||
#define UNREALDB_CRYPT_FILE_CHUNK_SIZE 4096
|
||||
|
||||
/** The salt length. Don't change. */
|
||||
#define UNREALDB_SALT_LEN 16
|
||||
|
||||
/** Database modes of operation (read or write) */
|
||||
typedef enum UnrealDBMode {
|
||||
UNREALDB_MODE_READ = 0,
|
||||
UNREALDB_MODE_WRITE = 1
|
||||
} UnrealDBMode;
|
||||
|
||||
typedef enum UnrealDBCipher {
|
||||
UNREALDB_CIPHER_XCHACHA20 = 0x0001
|
||||
} UnrealDBCipher;
|
||||
|
||||
typedef enum UnrealDBKDF {
|
||||
UNREALDB_KDF_ARGON2ID = 0x0001
|
||||
} UnrealDBKDF;
|
||||
|
||||
/** Database configuration for a particular file */
|
||||
typedef struct UnrealDBConfig {
|
||||
uint16_t kdf; /**< Key derivation function (always 0x01) */
|
||||
uint16_t t_cost; /**< Time cost (number of rounds) */
|
||||
uint16_t m_cost; /**< Memory cost (in number of bitshifts, eg 15 means 1<<15=32M) */
|
||||
uint16_t p_cost; /**< Parallel cost (number of concurrent threads) */
|
||||
uint16_t saltlen; /**< Length of the salt (normally UNREALDB_SALT_LEN) */
|
||||
char *salt; /**< Salt */
|
||||
uint16_t cipher; /**< Encryption cipher (always 0x01) */
|
||||
uint16_t keylen; /**< Key length */
|
||||
char *key; /**< The key used for encryption/decryption */
|
||||
} UnrealDBConfig;
|
||||
|
||||
typedef enum UnrealDBError {
|
||||
UNREALDB_ERROR_SUCCESS = 0, /**< Success, not an error */
|
||||
UNREALDB_ERROR_FILENOTFOUND = 1, /**< File does not exist */
|
||||
UNREALDB_ERROR_CRYPTED = 2, /**< File is crypted but no password provided */
|
||||
UNREALDB_ERROR_NOTCRYPTED = 3, /**< File is not crypted and a password was provided */
|
||||
UNREALDB_ERROR_HEADER = 4, /**< Header is corrupt, invalid or unknown format */
|
||||
UNREALDB_ERROR_SECRET = 5, /**< Invalid secret { } block provided - either does not exist or does not meet requirements */
|
||||
UNREALDB_ERROR_PASSWORD = 6, /**< Invalid password provided */
|
||||
UNREALDB_ERROR_IO = 7, /**< I/O error */
|
||||
UNREALDB_ERROR_API = 8, /**< API call violation, eg requesting to write on a file opened for reading */
|
||||
UNREALDB_ERROR_INTERNAL = 9, /**< Internal error, eg crypto routine returned something unexpected */
|
||||
} UnrealDBError;
|
||||
|
||||
/** Database handle.
|
||||
* This is returned by unrealdb_open() and used by all other unrealdb_* functions.
|
||||
*/
|
||||
typedef struct UnrealDB {
|
||||
FILE *fd; /**< File descriptor */
|
||||
UnrealDBMode mode; /**< UNREALDB_MODE_READ / UNREALDB_MODE_WRITE */
|
||||
int crypted; /**< Are we doing any encryption or just plaintext? */
|
||||
uint64_t creationtime; /**< When this file was created/updates */
|
||||
crypto_secretstream_xchacha20poly1305_state st; /**< Internal state for crypto engine */
|
||||
char buf[UNREALDB_CRYPT_FILE_CHUNK_SIZE]; /**< Buffer used for reading/writing */
|
||||
int buflen; /**< Length of current data in buffer */
|
||||
UnrealDBError error_code; /**< Last error code. Whenever this happens we will set this, never overwrite, and block further I/O */
|
||||
char *error_string; /**< Error string upon failure */
|
||||
UnrealDBConfig *config; /**< Config */
|
||||
} UnrealDB;
|
||||
|
||||
/** Used for speeding up reading/writing of DBs (so we don't have to run argon2 repeatedly) */
|
||||
typedef struct SecretCache SecretCache;
|
||||
struct SecretCache {
|
||||
SecretCache *prev, *next;
|
||||
UnrealDBConfig *config;
|
||||
time_t cache_hit;
|
||||
};
|
||||
|
||||
/** Used for storing secret { } blocks */
|
||||
struct Secret {
|
||||
Secret *prev, *next;
|
||||
char *name;
|
||||
char *password;
|
||||
SecretCache *cache;
|
||||
};
|
||||
|
||||
|
||||
/* tkl:
|
||||
* TKL_KILL|TKL_GLOBAL = Global K-Line (GLINE)
|
||||
|
||||
@@ -35,4 +35,4 @@
|
||||
#ifdef USE_LIBCURL
|
||||
#include <curl/curl.h>
|
||||
#endif
|
||||
#include <sodium.h>
|
||||
#include <argon2.h>
|
||||
|
||||
+4
-1
@@ -31,7 +31,7 @@ OBJS=dns.o auth.o channel.o crule.o dbuf.o \
|
||||
api-moddata.o api-extban.o api-isupport.o api-command.o \
|
||||
api-clicap.o api-messagetag.o api-history-backend.o api-efunctions.o \
|
||||
api-event.o \
|
||||
crypt_blowfish.o updconf.o crashreport.o modulemanager.o \
|
||||
crypt_blowfish.o unrealdb.o updconf.o crashreport.o modulemanager.o \
|
||||
utf8.o \
|
||||
openssl_hostname_validation.o $(URL)
|
||||
|
||||
@@ -224,6 +224,9 @@ api-efunctions.o: api-efunctions.c $(INCLUDES)
|
||||
crypt_blowfish.o: crypt_blowfish.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(BINCFLAGS) -c crypt_blowfish.c
|
||||
|
||||
unrealdb.o: unrealdb.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(BINCFLAGS) -c unrealdb.c
|
||||
|
||||
updconf.o: updconf.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(BINCFLAGS) -c updconf.c
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ ID_Copyright("(C) Carsten Munk 2001");
|
||||
MODVAR Event *events = NULL;
|
||||
|
||||
extern EVENT(unrealdns_removeoldrecords);
|
||||
extern EVENT(unrealdb_expire_secret_cache);
|
||||
|
||||
/** Add an event, a function that will run at regular intervals.
|
||||
* @param module Module that this event belongs to
|
||||
@@ -241,4 +242,5 @@ void SetupEvents(void)
|
||||
EventAdd(NULL, "handshake_timeout", handshake_timeout, NULL, 1000, 0);
|
||||
EventAdd(NULL, "try_connections", try_connections, NULL, 2000, 0);
|
||||
EventAdd(NULL, "tls_check_expiry", tls_check_expiry, NULL, (86400/2)*1000, 0);
|
||||
EventAdd(NULL, "unrealdb_expire_secret_cache", unrealdb_expire_secret_cache, NULL, 61000, 0);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#include "unrealircd.h"
|
||||
#include "crypt_blowfish.h"
|
||||
#include <argon2.h>
|
||||
|
||||
typedef struct AuthTypeList AuthTypeList;
|
||||
struct AuthTypeList {
|
||||
|
||||
+231
-3
@@ -71,6 +71,7 @@ static int _conf_help (ConfigFile *conf, ConfigEntry *ce);
|
||||
static int _conf_offchans (ConfigFile *conf, ConfigEntry *ce);
|
||||
static int _conf_sni (ConfigFile *conf, ConfigEntry *ce);
|
||||
static int _conf_security_group (ConfigFile *conf, ConfigEntry *ce);
|
||||
static int _conf_secret (ConfigFile *conf, ConfigEntry *ce);
|
||||
|
||||
/*
|
||||
* Validation commands
|
||||
@@ -104,6 +105,7 @@ static int _test_help (ConfigFile *conf, ConfigEntry *ce);
|
||||
static int _test_offchans (ConfigFile *conf, ConfigEntry *ce);
|
||||
static int _test_sni (ConfigFile *conf, ConfigEntry *ce);
|
||||
static int _test_security_group (ConfigFile *conf, ConfigEntry *ce);
|
||||
static int _test_secret (ConfigFile *conf, ConfigEntry *ce);
|
||||
|
||||
/* This MUST be alphabetized */
|
||||
static ConfigCommand _ConfigCommands[] = {
|
||||
@@ -128,6 +130,7 @@ static ConfigCommand _ConfigCommands[] = {
|
||||
{ "oper", _conf_oper, _test_oper },
|
||||
{ "operclass", _conf_operclass, _test_operclass },
|
||||
{ "require", _conf_require, _test_require },
|
||||
{ "secret", _conf_secret, _test_secret },
|
||||
{ "security-group", _conf_security_group, _test_security_group },
|
||||
{ "set", _conf_set, _test_set },
|
||||
{ "sni", _conf_sni, _test_sni },
|
||||
@@ -258,6 +261,7 @@ ConfigItem_blacklist_module *conf_blacklist_module = NULL;
|
||||
ConfigItem_help *conf_help = NULL;
|
||||
ConfigItem_offchans *conf_offchans = NULL;
|
||||
SecurityGroup *securitygroups = NULL;
|
||||
Secret *secrets = NULL;
|
||||
|
||||
MODVAR Configuration iConf;
|
||||
MODVAR Configuration tempiConf;
|
||||
@@ -2705,8 +2709,15 @@ int config_run()
|
||||
config_status("Running %s", cfptr->cf_filename);
|
||||
for (ce = cfptr->cf_entries; ce; ce = ce->ce_next)
|
||||
{
|
||||
if (!strcmp(ce->ce_varname, "set") || !strcmp(ce->ce_varname, "class"))
|
||||
continue; // already processed
|
||||
/* These are already processed above (set, class)
|
||||
* or via config_test() (secret).
|
||||
*/
|
||||
if (!strcmp(ce->ce_varname, "set") ||
|
||||
!strcmp(ce->ce_varname, "class") ||
|
||||
!strcmp(ce->ce_varname, "secret"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((cc = config_binary_search(ce->ce_varname))) {
|
||||
if ((cc->conffunc) && (cc->conffunc(cfptr, ce) < 0))
|
||||
@@ -2793,6 +2804,17 @@ int config_test()
|
||||
{
|
||||
if (config_verbose > 1)
|
||||
config_status("Testing %s", cfptr->cf_filename);
|
||||
/* First test and run the secret { } blocks */
|
||||
for (ce = cfptr->cf_entries; ce; ce = ce->ce_next)
|
||||
{
|
||||
if (!strcmp(ce->ce_varname, "secret"))
|
||||
{
|
||||
int n = _test_secret(cfptr, ce);
|
||||
errors += n;
|
||||
if (n == 0)
|
||||
_conf_secret(cfptr, ce);
|
||||
}
|
||||
}
|
||||
/* First test the set { } block */
|
||||
for (ce = cfptr->cf_entries; ce; ce = ce->ce_next)
|
||||
{
|
||||
@@ -2803,8 +2825,11 @@ int config_test()
|
||||
for (ce = cfptr->cf_entries; ce; ce = ce->ce_next)
|
||||
{
|
||||
/* These are already processed, so skip them here.. */
|
||||
if (!strcmp(ce->ce_varname, "set"))
|
||||
if (!strcmp(ce->ce_varname, "secret") ||
|
||||
!strcmp(ce->ce_varname, "set"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ((cc = config_binary_search(ce->ce_varname))) {
|
||||
if (cc->testfunc)
|
||||
errors += (cc->testfunc(cfptr, ce));
|
||||
@@ -10203,6 +10228,209 @@ int _conf_security_group(ConfigFile *conf, ConfigEntry *ce)
|
||||
return 1;
|
||||
}
|
||||
|
||||
Secret *find_secret(char *secret_name)
|
||||
{
|
||||
Secret *s;
|
||||
for (s = secrets; s; s = s->next)
|
||||
{
|
||||
if (!strcasecmp(s->name, secret_name))
|
||||
return s;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void free_secret_cache(SecretCache *c)
|
||||
{
|
||||
unrealdb_free_config(c->config);
|
||||
safe_free(c);
|
||||
}
|
||||
|
||||
void free_secret(Secret *s)
|
||||
{
|
||||
SecretCache *c, *c_next;
|
||||
for (c = s->cache; c; c = c_next)
|
||||
{
|
||||
c_next = c->next;
|
||||
DelListItem(c, s->cache);
|
||||
free_secret_cache(c);
|
||||
}
|
||||
safe_free(s->name);
|
||||
safe_free_sensitive(s->password);
|
||||
safe_free(s);
|
||||
}
|
||||
|
||||
char *_conf_secret_read_password(char *fname)
|
||||
{
|
||||
char *pwd, *err;
|
||||
int fd, n;
|
||||
|
||||
#ifndef _WIN32
|
||||
fd = open(fname, O_RDONLY);
|
||||
#else
|
||||
fd = open(fname, _O_RDONLY|_O_BINARY);
|
||||
#endif
|
||||
if (fd < 0)
|
||||
{
|
||||
/* This should not happen, as we tested for file exists earlier.. */
|
||||
config_error("Could not open file '%s': %s", fname, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pwd = safe_alloc_sensitive(512);
|
||||
n = read(fd, pwd, 511);
|
||||
if (n <= 0)
|
||||
{
|
||||
close(fd);
|
||||
config_error("Could not read from file '%s': %s", fname, strerror(errno));
|
||||
safe_free_sensitive(pwd);
|
||||
return NULL;
|
||||
}
|
||||
close(fd);
|
||||
stripcrlf(pwd);
|
||||
sodium_stackzero(1024);
|
||||
if (!valid_secret_password(pwd, &err))
|
||||
{
|
||||
config_error("Key from file '%s' does not meet password complexity requirements: %s", fname, err);
|
||||
safe_free_sensitive(pwd);
|
||||
return NULL;
|
||||
}
|
||||
return pwd;
|
||||
}
|
||||
|
||||
int _test_secret(ConfigFile *conf, ConfigEntry *ce)
|
||||
{
|
||||
int errors = 0;
|
||||
int has_password = 0, has_password_file = 0;
|
||||
ConfigEntry *cep;
|
||||
char *err;
|
||||
|
||||
if (!ce->ce_vardata)
|
||||
{
|
||||
config_error("%s:%i: secret block needs a name, eg: secret xyz {",
|
||||
ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
|
||||
errors++;
|
||||
} else {
|
||||
if (!security_group_valid_name(ce->ce_vardata))
|
||||
{
|
||||
config_error("%s:%i: secret block name '%s' contains invalid characters or is too long. "
|
||||
"Only letters, numbers, underscore and hyphen are allowed.",
|
||||
ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
for (cep = ce->ce_entries; cep; cep = cep->ce_next)
|
||||
{
|
||||
if (!strcmp(cep->ce_varname, "password"))
|
||||
{
|
||||
has_password = 1;
|
||||
CheckNull(cep);
|
||||
if (!valid_secret_password(cep->ce_vardata, &err))
|
||||
{
|
||||
config_error("%s:%d: secret::password does not meet password complexity requirements: %s",
|
||||
cep->ce_fileptr->cf_filename, cep->ce_varlinenum, err);
|
||||
errors++;
|
||||
}
|
||||
} else
|
||||
if (!strcmp(cep->ce_varname, "password-file"))
|
||||
{
|
||||
char *str;
|
||||
has_password_file = 1;
|
||||
CheckNull(cep);
|
||||
str = _conf_secret_read_password(cep->ce_vardata);
|
||||
if (!str)
|
||||
{
|
||||
config_error("%s:%d: secret::password-file: error reading password from file, see error from above.",
|
||||
cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
|
||||
errors++;
|
||||
}
|
||||
safe_free_sensitive(str);
|
||||
} else
|
||||
if (!strcmp(cep->ce_varname, "password-url"))
|
||||
{
|
||||
config_error("%s:%d: secret::password-url is not supported yet in this UnrealIRCd version.",
|
||||
cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
|
||||
errors++;
|
||||
} else
|
||||
{
|
||||
config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
|
||||
"secret", cep->ce_varname);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
if (cep->ce_entries)
|
||||
{
|
||||
config_error("%s:%d: secret::%s does not support sub-options (%s)",
|
||||
cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
|
||||
cep->ce_varname, cep->ce_entries->ce_varname);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_password && !has_password_file)
|
||||
{
|
||||
config_error("%s:%d: secret { } block must contain 1 of: password OR password-file",
|
||||
ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
|
||||
errors++;
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
/* NOTE: contrary to all other _conf* stuff, this one actually runs during config_test,
|
||||
* so during the early CONFIG TEST stage rather than CONFIG RUN.
|
||||
* This so all secret { } block configuration is available already during TEST/POSTTEST
|
||||
* stage for modules, so they can check if the password is correct or not.
|
||||
*/
|
||||
int _conf_secret(ConfigFile *conf, ConfigEntry *ce)
|
||||
{
|
||||
ConfigEntry *cep;
|
||||
Secret *s;
|
||||
Secret *existing;
|
||||
|
||||
s = safe_alloc(sizeof(Secret));
|
||||
safe_strdup(s->name, ce->ce_vardata);
|
||||
|
||||
for (cep = ce->ce_entries; cep; cep = cep->ce_next)
|
||||
{
|
||||
if (!strcmp(cep->ce_varname, "password"))
|
||||
{
|
||||
safe_strdup_sensitive(s->password, cep->ce_vardata);
|
||||
destroy_string(cep->ce_vardata); /* destroy the original */
|
||||
} else
|
||||
if (!strcmp(cep->ce_varname, "password-file"))
|
||||
{
|
||||
s->password = _conf_secret_read_password(cep->ce_vardata);
|
||||
}
|
||||
}
|
||||
|
||||
/* This may happen if we run twice, due to destroy_string() earlier: */
|
||||
if (BadPtr(s->password))
|
||||
{
|
||||
free_secret(s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If there is an existing secret { } block with this name in memory
|
||||
* and it has a different password, then free that secret block
|
||||
*/
|
||||
if ((existing = find_secret(s->name)))
|
||||
{
|
||||
if (!strcmp(s->password, existing->password))
|
||||
{
|
||||
free_secret(s);
|
||||
return 1;
|
||||
}
|
||||
/* passwords differ, so free the old existing one,
|
||||
* including purging the cache for it.
|
||||
*/
|
||||
DelListItem(existing, secrets);
|
||||
free_secret(existing);
|
||||
}
|
||||
AddListItem(s, secrets);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef USE_LIBCURL
|
||||
static void conf_download_complete(const char *url, const char *file, const char *errorbuf, int cached, void *inc_key)
|
||||
{
|
||||
|
||||
+9
-2
@@ -61,6 +61,7 @@ static void open_debugfile(), setup_signals();
|
||||
extern void init_glines(void);
|
||||
extern void tkl_init(void);
|
||||
extern void process_clients(void);
|
||||
extern void unrealdb_test(void);
|
||||
|
||||
#ifndef _WIN32
|
||||
MODVAR char **myargv;
|
||||
@@ -928,6 +929,11 @@ int InitUnrealIRCd(int argc, char *argv[])
|
||||
safe_strdup(configfile, CONFIGFILE);
|
||||
|
||||
init_random(); /* needs to be done very early!! */
|
||||
if (sodium_init() < 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to initialize sodium library -- error accessing random device?\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
memset(&botmotd, '\0', sizeof(MOTDFile));
|
||||
memset(&rules, '\0', sizeof(MOTDFile));
|
||||
@@ -1071,9 +1077,10 @@ int InitUnrealIRCd(int argc, char *argv[])
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
#if 1
|
||||
case 'S':
|
||||
charsys_dump_table(p ? p : "*");
|
||||
//charsys_dump_table(p ? p : "*");
|
||||
unrealdb_test();
|
||||
exit(0);
|
||||
#endif
|
||||
#ifndef _WIN32
|
||||
|
||||
+65
@@ -1974,3 +1974,68 @@ char *sendtype_to_cmd(SendType sendtype)
|
||||
return "TAGMSG";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Check password strength.
|
||||
* @param pass The password to check
|
||||
* @param min_length The minimum length of the password
|
||||
* @param strict Whether to require UPPER+lower+digits
|
||||
* @returns 1 if good, 0 if not.
|
||||
*/
|
||||
int check_password_strength(char *pass, int min_length, int strict, char **err)
|
||||
{
|
||||
char has_lowercase=0, has_uppercase=0, has_digit=0;
|
||||
char *p;
|
||||
static char buf[256];
|
||||
|
||||
if (err)
|
||||
*err = NULL;
|
||||
|
||||
if (strlen(pass) < min_length)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "Password must be at least %d characters", min_length);
|
||||
*err = buf;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (p=pass; *p; p++)
|
||||
{
|
||||
if (islower(*p))
|
||||
has_lowercase = 1;
|
||||
else if (isupper(*p))
|
||||
has_uppercase = 1;
|
||||
else if (isdigit(*p))
|
||||
has_digit = 1;
|
||||
}
|
||||
|
||||
if (strict)
|
||||
{
|
||||
if (!has_lowercase)
|
||||
{
|
||||
if (err)
|
||||
*err = "Password must contain at least 1 lowercase character";
|
||||
return 0;
|
||||
} else
|
||||
if (!has_uppercase)
|
||||
{
|
||||
if (err)
|
||||
*err = "Password must contain at least 1 UPPERcase character";
|
||||
return 0;
|
||||
} else
|
||||
if (!has_digit)
|
||||
{
|
||||
if (err)
|
||||
*err = "Password must contain at least 1 digit (number)";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int valid_secret_password(char *pass, char **err)
|
||||
{
|
||||
return check_password_strength(pass, 10, 1, err);
|
||||
}
|
||||
|
||||
@@ -724,6 +724,34 @@ void outofmemory(size_t bytes)
|
||||
exit(7);
|
||||
}
|
||||
|
||||
/** Allocate sensitive memory - this should only be used for HIGHLY sensitive data, since
|
||||
* it wastes 8192+ bytes even if only asked to allocate for example 32 bytes (this is by design).
|
||||
* @param size How many bytes to allocate
|
||||
* @returns A pointer to the newly allocated memory.
|
||||
* @note If out of memory then the IRCd will exit.
|
||||
*/
|
||||
void *safe_alloc_sensitive(size_t size)
|
||||
{
|
||||
void *p;
|
||||
if (size == 0)
|
||||
return NULL;
|
||||
p = sodium_malloc(((size/32)*32)+32);
|
||||
if (!p)
|
||||
outofmemory(size);
|
||||
memset(p, 0, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
/** Safely duplicate a string */
|
||||
char *our_strdup_sensitive(const char *str)
|
||||
{
|
||||
char *ret = safe_alloc_sensitive(strlen(str)+1);
|
||||
if (!ret)
|
||||
outofmemory(strlen(str));
|
||||
strcpy(ret, str); /* safe, see above */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Returns a unique filename in the specified directory
|
||||
* using the specified suffix. The returned value will
|
||||
* be of the form <dir>/<random-hex>.<suffix>
|
||||
|
||||
+1040
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user