Skip to content

Commit

Permalink
#46 Added secure password algorythm (#720)
Browse files Browse the repository at this point in the history
* Update starterdb wf with bugfix applied to fuzzball muf

* #46 finally finsihed strong hashed passwords
  • Loading branch information
tanabi authored Jan 28, 2024
1 parent 7821f5c commit 1a132b2
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 12 deletions.
1 change: 1 addition & 0 deletions docs/man.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4610,6 +4610,7 @@ Parameters available:
(bool) ignore_support - Enable support for @ignoring players
(int) instr_slice - Max. uninterrupted instructions per timeslice
(str) leave_mesg - Logoff message for QUIT
(bool) legacy_password_hash - Use Legacy (insecure, compatible) Password Hash
(int) link_cost - Cost to link an exit
(int) listen_mlev - Mucker Level required for Listener programs
(bool) lock_envcheck - Locks check environment for properties
Expand Down
1 change: 1 addition & 0 deletions docs/mufman.html
Original file line number Diff line number Diff line change
Expand Up @@ -7337,6 +7337,7 @@ <h3 id="sysparm">SYSPARM ( s -- s )
(bool) ignore_support - Enable support for @ignoring players
(int) instr_slice - Max. uninterrupted instructions per timeslice
(str) leave_mesg - Logoff message for QUIT
(bool) legacy_password_hash - Use Legacy (insecure, compatible) Password Hash
(int) link_cost - Cost to link an exit
(int) listen_mlev - Mucker Level required for Listener programs
(bool) lock_envcheck - Locks check environment for properties
Expand Down
26 changes: 26 additions & 0 deletions include/fbmath.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,32 @@ void MD5hex(void *dest, const void *orig, size_t len);
*/
int no_good(double test);

/**
* Generate a PBKDF2 password hash with the given password and salt.
*
* If salt is passed as NULL, we will generate a random 10 byte salt.
*
* If the MUCK wasn't compiled with SSL, this will transparently
* run MD5base64.
*
* The buffer provided should be at least 40 characters long, but
* bigger is better. 128 should be pretty good. The entire buffer
* will be filled with 1 byte to spare if the hash is run (if not,
* @see MD5base64). If you provide your own salt, the buffer must
* be large enough to contain the seed + 4 bytes.
*
* Seed cannot contain a $ symbol as that is reserved.
*
* @param password the password to hash
* @param password_len the strlen of the password
* @param salt the salt portion of the hash
* @param salt_len the length of the salt
* @param buffer the buffer to put the result into
* @param buffer_len the size of the buffer
*/
void pbkdf2_hash(const char* password, int password_len, const char* salt,
int salt_len, char* buffer, int buffer_len);

/**
* Do a seeded random number generation
*
Expand Down
1 change: 1 addition & 0 deletions include/tune.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ extern bool tp_ignore_bidirectional; /**< Tune variable */
extern bool tp_ignore_support; /**< Tune variable */
extern int tp_instr_slice; /**< Tune variable */
extern const char *tp_leave_mesg; /**< Tune variable */
extern bool tp_legacy_password_hash; /**< Tune variable */
extern int tp_link_cost; /**< Tune variable */
extern int tp_listen_mlev; /**< Tune variable */
extern bool tp_lock_envcheck; /**< Tune variable */
Expand Down
13 changes: 13 additions & 0 deletions include/tunelist.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ bool tp_ignore_bidirectional; /**> Described below */
bool tp_ignore_support; /**> Described below */
int tp_instr_slice; /**> Described below */
const char *tp_leave_mesg; /**> Described below */
bool tp_legacy_password_hash; /**> Described below */
int tp_link_cost; /**> Described below */
int tp_listen_mlev; /**> Described below */
bool tp_lock_envcheck; /**> Described below */
Expand Down Expand Up @@ -1161,6 +1162,18 @@ struct tune_entry tune_list[] = {
MLEV_WIZARD,
true
},
{
"legacy_password_hash",
"Use Legacy (insecure, compatible) Password Hash",
"Misc",
"",
TP_TYPE_BOOLEAN,
.defaultval.b=false,
.currentval.b=&tp_legacy_password_hash,
MLEV_WIZARD,
MLEV_WIZARD,
true
},
{
"link_cost",
"Cost to link an exit",
Expand Down
121 changes: 120 additions & 1 deletion src/fbmath.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
#include "fbmath.h"
#include "inst.h"

#ifdef USE_SSL
# ifdef HAVE_OPENSSL
# include <openssl/evp.h>
# include <openssl/sha.h>
# else
# include <evp.h>
# include <sha.h>
# endif
#endif

/**
* Generate a random floating point number
*
Expand Down Expand Up @@ -106,6 +116,12 @@ no_good(double test)
*
**************************************************************************/

/*
* We will use this in a couple places.
*/
static const unsigned char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";


/* The four core functions - F1 is optimized somewhat */

/* #define F1(x, y, z) (x & y | ~x & z) */
Expand Down Expand Up @@ -388,7 +404,6 @@ MD5hash(void *dest, const void *orig, size_t len)
static void
Base64Encode(char *outbuf, const void *inbuf, size_t inlen)
{
const unsigned char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const unsigned char *inb = inbuf;
unsigned char *out = NULL;
size_t numb;
Expand Down Expand Up @@ -562,3 +577,107 @@ rnd(void *buffer)
MD5hash(digest, digest, sizeof(digest));
return (digest[0]);
}

/*********************************************************************
*
* PBKDF2 Password Hashing stuff
*
*********************************************************************/

#ifdef USE_SSL
static void
PBKDF2_HMAC_SHA_512(const char* pass, const unsigned char* salt,
int32_t iterations, uint32_t outputBytes,
char* hexResult)
{
unsigned int i;
unsigned char digest[outputBytes];
PKCS5_PBKDF2_HMAC(pass, strlen(pass), salt, strlen(salt), iterations,
EVP_sha512(), outputBytes, digest);
for (i = 0; i < sizeof(digest); i++)
sprintf(hexResult + (i * 2), "%02x", 255 & digest[i]);
}
#endif

/**
* Generate a PBKDF2 password hash with the given password and salt.
*
* If salt is passed as NULL, we will generate a random 10 byte salt.
*
* If the MUCK wasn't compiled with SSL, this will transparently
* run MD5base64.
*
* The buffer provided should be at least 40 characters long, but
* bigger is better. 128 should be pretty good. The entire buffer
* will be filled with 1 byte to spare if the hash is run (if not,
* @see MD5base64). If you provide your own salt, the buffer must
* be large enough to contain the seed + 4 bytes.
*
* Seed cannot contain a $ symbol as that is reserved.
*
* @param password the password to hash
* @param password_len the strlen of the password
* @param salt the salt portion of the hash
* @param salt_len the length of the salt
* @param buffer the buffer to put the result into
* @param buffer_len the size of the buffer
*/
void
pbkdf2_hash(const char* password, int password_len, const char* salt,
int salt_len, char* buffer, int buffer_len)
{
#ifdef USE_SSL
char salt_buf[11];
unsigned int i, digest_len;
unsigned char* digest;

/*
* Generate a salt if we need to
*/
if (!salt) {
for (i = 0; i < 10; i++) {
salt_buf[i] = b64[RANDOM()%sizeof(b64)];
}

salt_buf[10] = '\0';

salt = salt_buf;
salt_len = 10;
}

/*
* Clear the buffer
*/
memset(buffer, 0, buffer_len);

/*
* Copy the salt into the buffer along with the markers.
*/
snprintf(buffer, buffer_len, "$1$%s$", salt);

/*
* Calculate our digest size
*/
digest_len = ((buffer_len - salt_len - 4)/2);
digest = (unsigned char*)malloc(digest_len+1);

/*
* Generate a hash with the rest of the buffer. Use 1000 iterations.
*/
PKCS5_PBKDF2_HMAC(password, password_len, salt, salt_len, 1000,
EVP_sha512(), digest_len, digest);

for (i = 0; i < digest_len; i++) {
sprintf(buffer + salt_len + 4 + (i * 2), "%02x", 255 & digest[i]);
}

free(digest);

/*
* That should be it! Fingers crossed
*/

#else
MD5base64(buffer, password, password_len);
#endif
}
72 changes: 61 additions & 11 deletions src/player.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,23 +79,50 @@ lookup_player(const char *name)
int
check_password(dbref player, const char *password)
{
char md5buf[64];
char md5buf[128];
int len_hash = 0;
int len_salt = 0;

const char* salt;

const char *processed = password;
const char *pword = PLAYER_PASSWORD(player);

if (password == NULL) {
MD5base64(md5buf, "", 0);
if (!pword || !*pword) {
return 1;
}

/*
* Get the hash length
*/
len_hash = strlen(pword);

/*
* Is it a seeded hash? If so, let's extract the seed.
*/
if ((len_hash > 4) && (!strncmp(pword, "$1$", 3))) {
/* Figure out the seed */
salt = pword + 3;

for ( ; (salt[len_salt] != '$') && ((len_salt+3) < len_hash);
len_salt++) { }

pbkdf2_hash(password, strlen(password), salt, len_salt, md5buf,
sizeof(md5buf));

processed = md5buf;
} else {
if (*password) {
MD5base64(md5buf, password, strlen(password));
if (password == NULL) {
MD5base64(md5buf, "", 0);
processed = md5buf;
} else {
if (*password) {
MD5base64(md5buf, password, strlen(password));
processed = md5buf;
}
}
}

if (!pword || !*pword)
return 1;

if (!strcmp(pword, processed))
return 1;

Expand Down Expand Up @@ -128,11 +155,17 @@ set_password_raw(dbref player, const char *password)
void
set_password(dbref player, const char *password)
{
char md5buf[64];
char md5buf[128];
const char *processed = password;

if (*password) {
MD5base64(md5buf, password, strlen(password));
if (tp_legacy_password_hash) {
MD5base64(md5buf, password, strlen(password));
} else {
pbkdf2_hash(password, strlen(password), NULL, 0, md5buf,
sizeof(md5buf));
}

processed = md5buf;
}

Expand Down Expand Up @@ -375,7 +408,13 @@ void
delete_player(dbref who)
{
int result;
char buf[BUFFER_LEN];
/*
* TODO: I increased the buffer size here to stifle a warning.
* The underlying error is namebuf is actually too large.
* The snprinft below for "Renaming %s(#%d)..." was throwing
* a warning.
*/
char buf[BUFFER_LEN+BUFFER_LEN];
char namebuf[BUFFER_LEN];
dbref found, ren;
int j;
Expand All @@ -398,6 +437,17 @@ delete_player(dbref who)
ren = (i == who) ? found : i;
j = 0;

/*
* TODO: This, technically, can enable player names
* to exceed the max name length. I'm not sure
* we care since this should be a super rare
* occasion? But exceeding the max name length
* could cause chaos in other areas.
*
* I'm not even sure how to test this, it seems
* like it would be from a catastrophic DB failure.
*/

do {
snprintf(namebuf, sizeof(namebuf), "%s%d", NAME(ren),
++j);
Expand Down

0 comments on commit 1a132b2

Please sign in to comment.