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

#46 Added secure password algorythm #720

Merged
merged 3 commits into from
Jan 28, 2024
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
21 changes: 19 additions & 2 deletions dbs/starterdb/muf/34.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
Version history
1.001, 16 March 2003: don't use .rtimestr macro, use rtimestr in
$lib/timestr. Default USE_ATLASTON to on.

1.002, 29 August 2023: if a stale dbref is on your wf list [a toaded player]
then laston #wf would fail with a crash error. Added a filtering for
non-player refs on the wf list. [HopeIslandCoder]
)
$author Natasha O'Brien
$version 1.001
$version 1.002
$note A Fuzzball 6 laston.

$include $lib/strings
Expand Down Expand Up @@ -74,7 +78,20 @@
pmatch dup ok? if 1 array_make else pop 0 array_make then
$endif
;
: do-watchfor pop me @ prop_wflist array_get_reflist ;
: do-watchfor
pop me @ prop_wflist array_get_reflist

( Filter out invalid ref's )
0 array_make
swap foreach
swap pop
dup player? if
swap array_appenditem
else
pop
then
repeat
;
: do-room ( strY -- arr )
pop loc @ contents_array ( arrContents )
0 array_make swap foreach swap pop ( arrPlayers db )
Expand Down
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 @@ -111,6 +111,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 @@ -1177,6 +1178,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 @@ -362,7 +395,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 @@ -385,6 +424,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
Loading