forked from github/server
Merge branch 'upstream-master'
Conflicts: src/kernel/save.test.c src/kernel/version.h
This commit is contained in:
commit
4f01d7041f
17 changed files with 286 additions and 80 deletions
2
crypto
2
crypto
|
@ -1 +1 @@
|
||||||
Subproject commit e0f9891a91d69c042f82c1e13e48ab4c7160381d
|
Subproject commit 93dc9200fa4cb6bfa3883b19f6d33fd416ca43da
|
|
@ -1,3 +1,13 @@
|
||||||
|
{
|
||||||
|
strcpy.S:197
|
||||||
|
Memcheck:Cond
|
||||||
|
obj:/lib/x86_64-linux-gnu/libc-2.13.so
|
||||||
|
}
|
||||||
|
{
|
||||||
|
strcpy.S:1106
|
||||||
|
Memcheck:Value8
|
||||||
|
obj:/lib/x86_64-linux-gnu/libc-2.13.so
|
||||||
|
}
|
||||||
{
|
{
|
||||||
stpncpy sse3
|
stpncpy sse3
|
||||||
Memcheck:Cond
|
Memcheck:Cond
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
{
|
||||||
|
stpncpy in strcpy-sse2-unaligned.S:1659
|
||||||
|
Memcheck:Value8
|
||||||
|
fun:__stpncpy_sse2_unaligned
|
||||||
|
}
|
||||||
# old zlib version
|
# old zlib version
|
||||||
{
|
{
|
||||||
zlib1g-dev-1:1.2.3.4.dfsg
|
zlib1g-dev-1:1.2.3.4.dfsg
|
||||||
|
|
|
@ -392,7 +392,7 @@ static int tolua_faction_set_password(lua_State * L)
|
||||||
{
|
{
|
||||||
faction *self = (faction *)tolua_tousertype(L, 1, 0);
|
faction *self = (faction *)tolua_tousertype(L, 1, 0);
|
||||||
const char * passw = tolua_tostring(L, 2, 0);
|
const char * passw = tolua_tostring(L, 2, 0);
|
||||||
faction_setpassword(self, password_hash(passw, 0, PASSWORD_DEFAULT));
|
faction_setpassword(self, password_encode(passw, PASSWORD_DEFAULT));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
#define VERSION_MAJOR 3
|
#define VERSION_MAJOR 3
|
||||||
#define VERSION_MINOR 8
|
#define VERSION_MINOR 8
|
||||||
#define VERSION_BUILD 3
|
#define VERSION_BUILD 4
|
||||||
|
|
|
@ -252,7 +252,7 @@ faction *addfaction(const char *email, const char *password,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!password) password = itoa36(rng_int());
|
if (!password) password = itoa36(rng_int());
|
||||||
faction_setpassword(f, password_hash(password, 0, PASSWORD_DEFAULT));
|
faction_setpassword(f, password_encode(password, PASSWORD_DEFAULT));
|
||||||
ADDMSG(&f->msgs, msg_message("changepasswd", "value", password));
|
ADDMSG(&f->msgs, msg_message("changepasswd", "value", password));
|
||||||
|
|
||||||
f->alliance_joindate = turn;
|
f->alliance_joindate = turn;
|
||||||
|
@ -566,7 +566,8 @@ void faction_setbanner(faction * self, const char *banner)
|
||||||
|
|
||||||
void faction_setpassword(faction * f, const char *pwhash)
|
void faction_setpassword(faction * f, const char *pwhash)
|
||||||
{
|
{
|
||||||
assert(pwhash && pwhash[0] == '$');
|
assert(pwhash);
|
||||||
|
// && pwhash[0] == '$');
|
||||||
free(f->_password);
|
free(f->_password);
|
||||||
f->_password = _strdup(pwhash);
|
f->_password = _strdup(pwhash);
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,12 +108,12 @@ static void test_addfaction(CuTest *tc) {
|
||||||
CuAssertPtrEquals(tc, NULL, (void *)f->ursprung);
|
CuAssertPtrEquals(tc, NULL, (void *)f->ursprung);
|
||||||
CuAssertPtrEquals(tc, (void *)factions, (void *)f);
|
CuAssertPtrEquals(tc, (void *)factions, (void *)f);
|
||||||
CuAssertStrEquals(tc, "test@eressea.de", f->email);
|
CuAssertStrEquals(tc, "test@eressea.de", f->email);
|
||||||
CuAssertIntEquals(tc, true, checkpasswd(f, "hurrdurr"));
|
CuAssertTrue(tc, checkpasswd(f, "hurrdurr"));
|
||||||
CuAssertPtrEquals(tc, (void *)lang, (void *)f->locale);
|
CuAssertPtrEquals(tc, (void *)lang, (void *)f->locale);
|
||||||
CuAssertIntEquals(tc, 1234, f->subscription);
|
CuAssertIntEquals(tc, 1234, f->subscription);
|
||||||
CuAssertIntEquals(tc, 0, f->flags);
|
CuAssertIntEquals(tc, 0, f->flags);
|
||||||
CuAssertIntEquals(tc, 0, f->age);
|
CuAssertIntEquals(tc, 0, f->age);
|
||||||
CuAssertIntEquals(tc, true, faction_alive(f));
|
CuAssertTrue(tc, faction_alive(f));
|
||||||
CuAssertIntEquals(tc, M_GRAY, f->magiegebiet);
|
CuAssertIntEquals(tc, M_GRAY, f->magiegebiet);
|
||||||
CuAssertIntEquals(tc, turn, f->lastorders);
|
CuAssertIntEquals(tc, turn, f->lastorders);
|
||||||
CuAssertPtrEquals(tc, f, findfaction(f->no));
|
CuAssertPtrEquals(tc, f, findfaction(f->no));
|
||||||
|
@ -124,10 +124,10 @@ static void test_check_passwd(CuTest *tc) {
|
||||||
faction *f;
|
faction *f;
|
||||||
|
|
||||||
f = test_create_faction(0);
|
f = test_create_faction(0);
|
||||||
faction_setpassword(f, password_hash("password", 0, PASSWORD_DEFAULT));
|
faction_setpassword(f, password_encode("password", PASSWORD_DEFAULT));
|
||||||
CuAssertIntEquals(tc, true, checkpasswd(f, "password"));
|
CuAssertTrue(tc, checkpasswd(f, "password"));
|
||||||
CuAssertIntEquals(tc, false, checkpasswd(f, "assword"));
|
CuAssertTrue(tc, !checkpasswd(f, "assword"));
|
||||||
CuAssertIntEquals(tc, false, checkpasswd(f, "PASSWORD"));
|
CuAssertTrue(tc, !checkpasswd(f, "PASSWORD"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_get_monsters(CuTest *tc) {
|
static void test_get_monsters(CuTest *tc) {
|
||||||
|
|
|
@ -1155,6 +1155,58 @@ void write_spellbook(const struct spellbook *book, struct storage *store)
|
||||||
WRITE_TOK(store, "end");
|
WRITE_TOK(store, "end");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char * getpasswd(int fno) {
|
||||||
|
const char *prefix = itoa36(fno);
|
||||||
|
size_t len = strlen(prefix);
|
||||||
|
FILE * F = fopen("passwords.txt", "r");
|
||||||
|
char line[80];
|
||||||
|
if (F) {
|
||||||
|
while (!feof(F)) {
|
||||||
|
fgets(line, sizeof(line), F);
|
||||||
|
if (line[len]==':' && strncmp(prefix, line, len)==0) {
|
||||||
|
size_t slen = strlen(line)-1;
|
||||||
|
assert(line[slen]=='\n');
|
||||||
|
line[slen] = 0;
|
||||||
|
fclose(F);
|
||||||
|
return _strdup(line+len+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(F);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void read_password(gamedata *data, faction *f) {
|
||||||
|
char name[128];
|
||||||
|
READ_STR(data->store, name, sizeof(name));
|
||||||
|
if (data->version == BADCRYPT_VERSION) {
|
||||||
|
char * pass = getpasswd(f->no);
|
||||||
|
if (pass) {
|
||||||
|
faction_setpassword(f, password_encode(pass, PASSWORD_DEFAULT));
|
||||||
|
free(pass); // TODO: remove this allocation!
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
free(f->_password);
|
||||||
|
f->_password = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
faction_setpassword(f, (data->version >= CRYPT_VERSION) ? name : password_encode(name, PASSWORD_DEFAULT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _test_read_password(gamedata *data, faction *f) {
|
||||||
|
read_password(data, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_password(gamedata *data, const faction *f) {
|
||||||
|
WRITE_TOK(data->store, (const char *)f->_password);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _test_write_password(gamedata *data, const faction *f) {
|
||||||
|
write_password(data, f);
|
||||||
|
}
|
||||||
|
|
||||||
/** Reads a faction from a file.
|
/** Reads a faction from a file.
|
||||||
* This function requires no context, can be called in any state. The
|
* This function requires no context, can be called in any state. The
|
||||||
* faction may not already exist, however.
|
* faction may not already exist, however.
|
||||||
|
@ -1221,8 +1273,7 @@ faction *readfaction(struct gamedata * data)
|
||||||
set_email(&f->email, "");
|
set_email(&f->email, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
READ_STR(data->store, name, sizeof(name));
|
read_password(data, f);
|
||||||
faction_setpassword(f, (data->version > CRYPT_VERSION) ? name : password_hash(name, 0, PASSWORD_DEFAULT));
|
|
||||||
if (data->version < NOOVERRIDE_VERSION) {
|
if (data->version < NOOVERRIDE_VERSION) {
|
||||||
READ_STR(data->store, 0, 0);
|
READ_STR(data->store, 0, 0);
|
||||||
}
|
}
|
||||||
|
@ -1330,7 +1381,7 @@ void writefaction(struct gamedata *data, const faction * f)
|
||||||
WRITE_STR(data->store, f->name);
|
WRITE_STR(data->store, f->name);
|
||||||
WRITE_STR(data->store, f->banner);
|
WRITE_STR(data->store, f->banner);
|
||||||
WRITE_STR(data->store, f->email);
|
WRITE_STR(data->store, f->email);
|
||||||
WRITE_TOK(data->store, f->_password);
|
write_password(data, f);
|
||||||
WRITE_TOK(data->store, locale_name(f->locale));
|
WRITE_TOK(data->store, locale_name(f->locale));
|
||||||
WRITE_INT(data->store, f->lastorders);
|
WRITE_INT(data->store, f->lastorders);
|
||||||
WRITE_INT(data->store, f->age);
|
WRITE_INT(data->store, f->age);
|
||||||
|
|
|
@ -83,6 +83,9 @@ extern "C" {
|
||||||
struct gamedata *gamedata_open(const char *filename, const char *mode);
|
struct gamedata *gamedata_open(const char *filename, const char *mode);
|
||||||
void gamedata_close(struct gamedata *data);
|
void gamedata_close(struct gamedata *data);
|
||||||
|
|
||||||
|
/* test-only functions that give access to internal implementation details (BAD) */
|
||||||
|
void _test_write_password(struct gamedata *data, const struct faction *f);
|
||||||
|
void _test_read_password(struct gamedata *data, struct faction *f);
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -14,9 +14,14 @@
|
||||||
#include <triggers/timeout.h>
|
#include <triggers/timeout.h>
|
||||||
#include <util/attrib.h>
|
#include <util/attrib.h>
|
||||||
#include <util/event.h>
|
#include <util/event.h>
|
||||||
|
#include <util/base36.h>
|
||||||
|
#include <util/password.h>
|
||||||
|
|
||||||
#include <CuTest.h>
|
#include <CuTest.h>
|
||||||
#include <tests.h>
|
#include <tests.h>
|
||||||
|
|
||||||
|
#include <storage.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
static void test_readwrite_data(CuTest * tc)
|
static void test_readwrite_data(CuTest * tc)
|
||||||
|
@ -51,15 +56,17 @@ static void test_readwrite_unit(CuTest * tc)
|
||||||
u = test_create_unit(f, r);
|
u = test_create_unit(f, r);
|
||||||
join_path(datapath(), filename, path, sizeof(path));
|
join_path(datapath(), filename, path, sizeof(path));
|
||||||
|
|
||||||
data = gamedata_open(path, "w");
|
data = gamedata_open(path, "wb");
|
||||||
CuAssertPtrNotNull(tc, data); // TODO: intermittent test
|
CuAssertPtrNotNull(tc, data);
|
||||||
|
|
||||||
write_unit(data, u);
|
write_unit(data, u);
|
||||||
gamedata_close(data);
|
gamedata_close(data);
|
||||||
|
|
||||||
free_gamedata();
|
free_gamedata();
|
||||||
f = test_create_faction(0);
|
f = test_create_faction(0);
|
||||||
renumber_faction(f, fno);
|
renumber_faction(f, fno);
|
||||||
data = gamedata_open(path, "r");
|
data = gamedata_open(path, "rb");
|
||||||
|
CuAssertPtrNotNull(tc, data);
|
||||||
u = read_unit(data);
|
u = read_unit(data);
|
||||||
gamedata_close(data);
|
gamedata_close(data);
|
||||||
|
|
||||||
|
@ -195,6 +202,55 @@ static void test_readwrite_dead_faction_createunit(CuTest *tc) {
|
||||||
test_cleanup();
|
test_cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_read_password(CuTest *tc) {
|
||||||
|
const char *path = "test.dat";
|
||||||
|
gamedata *data;
|
||||||
|
faction *f;
|
||||||
|
f = test_create_faction(0);
|
||||||
|
faction_setpassword(f, password_encode("secret", PASSWORD_DEFAULT));
|
||||||
|
data = gamedata_open(path, "wb");
|
||||||
|
CuAssertPtrNotNull(tc, data);
|
||||||
|
_test_write_password(data, f);
|
||||||
|
gamedata_close(data);
|
||||||
|
data = gamedata_open(path, "rb");
|
||||||
|
CuAssertPtrNotNull(tc, data);
|
||||||
|
_test_read_password(data, f);
|
||||||
|
gamedata_close(data);
|
||||||
|
CuAssertTrue(tc, checkpasswd(f, "secret"));
|
||||||
|
CuAssertIntEquals(tc, 0, remove(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_read_password_external(CuTest *tc) {
|
||||||
|
const char *path = "test.dat", *pwfile = "passwords.txt";
|
||||||
|
gamedata *data;
|
||||||
|
faction *f;
|
||||||
|
FILE * F;
|
||||||
|
|
||||||
|
remove(pwfile);
|
||||||
|
f = test_create_faction(0);
|
||||||
|
faction_setpassword(f, password_encode("secret", PASSWORD_DEFAULT));
|
||||||
|
CuAssertPtrNotNull(tc, f->_password);
|
||||||
|
data = gamedata_open(path, "wb");
|
||||||
|
CuAssertPtrNotNull(tc, data);
|
||||||
|
WRITE_TOK(data->store, (const char *)f->_password);
|
||||||
|
WRITE_TOK(data->store, (const char *)f->_password);
|
||||||
|
gamedata_close(data);
|
||||||
|
data = gamedata_open(path, "rb");
|
||||||
|
CuAssertPtrNotNull(tc, data);
|
||||||
|
data->version = BADCRYPT_VERSION;
|
||||||
|
_test_read_password(data, f);
|
||||||
|
CuAssertPtrEquals(tc, 0, f->_password);
|
||||||
|
F = fopen(pwfile, "wt");
|
||||||
|
fprintf(F, "%s:secret\n", itoa36(f->no));
|
||||||
|
fclose(F);
|
||||||
|
_test_read_password(data, f);
|
||||||
|
CuAssertPtrNotNull(tc, f->_password);
|
||||||
|
gamedata_close(data);
|
||||||
|
CuAssertTrue(tc, checkpasswd(f, "secret"));
|
||||||
|
CuAssertIntEquals(tc, 0, remove(path));
|
||||||
|
CuAssertIntEquals(tc, 0, remove(pwfile));
|
||||||
|
}
|
||||||
|
|
||||||
CuSuite *get_save_suite(void)
|
CuSuite *get_save_suite(void)
|
||||||
{
|
{
|
||||||
CuSuite *suite = CuSuiteNew();
|
CuSuite *suite = CuSuiteNew();
|
||||||
|
@ -204,5 +260,7 @@ CuSuite *get_save_suite(void)
|
||||||
SUITE_ADD_TEST(suite, test_readwrite_dead_faction_changefaction);
|
SUITE_ADD_TEST(suite, test_readwrite_dead_faction_changefaction);
|
||||||
SUITE_ADD_TEST(suite, test_readwrite_dead_faction_regionowner);
|
SUITE_ADD_TEST(suite, test_readwrite_dead_faction_regionowner);
|
||||||
DISABLE_TEST(suite, test_readwrite_dead_faction_group);
|
DISABLE_TEST(suite, test_readwrite_dead_faction_group);
|
||||||
|
SUITE_ADD_TEST(suite, test_read_password);
|
||||||
|
SUITE_ADD_TEST(suite, test_read_password_external);
|
||||||
return suite;
|
return suite;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,9 +33,10 @@
|
||||||
#define SPELL_LEVEL_VERSION 348 /* f->max_spelllevel gets stored, not calculated */
|
#define SPELL_LEVEL_VERSION 348 /* f->max_spelllevel gets stored, not calculated */
|
||||||
#define OWNER_3_VERSION 349 /* regions store last owner, not last alliance */
|
#define OWNER_3_VERSION 349 /* regions store last owner, not last alliance */
|
||||||
#define ATTRIBOWNER_VERSION 350 /* all attrib_type functions know who owns the attribute */
|
#define ATTRIBOWNER_VERSION 350 /* all attrib_type functions know who owns the attribute */
|
||||||
#define CRYPT_VERSION 351 /* passwords are encrypted (3.8.2), plane.watchers are gone (3.8.3) */
|
#define BADCRYPT_VERSION 351 /* passwords are encrypted, poorly */
|
||||||
#define RELEASE_VERSION CRYPT_VERSION /* current datafile */
|
#define CRYPT_VERSION 352 /* passwords are encrypted */
|
||||||
|
#define RELEASE_VERSION ATTRIBOWNER_VERSION /* current datafile */
|
||||||
#define MIN_VERSION INTPAK_VERSION /* minimal datafile we support */
|
#define MIN_VERSION INTPAK_VERSION /* minimal datafile we support */
|
||||||
#define MAX_VERSION RELEASE_VERSION /* change this if we can need to read the future datafile, and we can do so */
|
#define MAX_VERSION BADCRYPT_VERSION /* change this if we can need to read the future datafile, and we can do so */
|
||||||
|
|
||||||
#define STREAM_VERSION 2 /* internal encoding of binary files */
|
#define STREAM_VERSION 2 /* internal encoding of binary files */
|
||||||
|
|
|
@ -2172,7 +2172,7 @@ int password_cmd(unit * u, struct order *ord)
|
||||||
cmistake(u, ord, 283, MSG_EVENT);
|
cmistake(u, ord, 283, MSG_EVENT);
|
||||||
strlcpy(pwbuf, itoa36(rng_int()), sizeof(pwbuf));
|
strlcpy(pwbuf, itoa36(rng_int()), sizeof(pwbuf));
|
||||||
}
|
}
|
||||||
faction_setpassword(u->faction, password_hash(pwbuf, 0, PASSWORD_DEFAULT));
|
faction_setpassword(u->faction, password_encode(pwbuf, PASSWORD_DEFAULT));
|
||||||
ADDMSG(&u->faction->msgs, msg_message("changepasswd",
|
ADDMSG(&u->faction->msgs, msg_message("changepasswd",
|
||||||
"value", pwbuf));
|
"value", pwbuf));
|
||||||
return 0;
|
return 0;
|
||||||
|
|
25
src/util/gamedata.test.c
Normal file
25
src/util/gamedata.test.c
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#include <platform.h>
|
||||||
|
#include "gamedata.h"
|
||||||
|
|
||||||
|
#include <CuTest.h>
|
||||||
|
#include <tests.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
static void test_gamedata(CuTest * tc)
|
||||||
|
{
|
||||||
|
gamedata *data;
|
||||||
|
data = gamedata_open("test.dat", "wb", 0);
|
||||||
|
CuAssertPtrNotNull(tc, data);
|
||||||
|
gamedata_close(data);
|
||||||
|
data = gamedata_open("test.dat", "rb", 0);
|
||||||
|
CuAssertPtrNotNull(tc, data);
|
||||||
|
gamedata_close(data);
|
||||||
|
CuAssertIntEquals(tc, 0, remove("test.dat"));
|
||||||
|
}
|
||||||
|
|
||||||
|
CuSuite *get_gamedata_suite(void)
|
||||||
|
{
|
||||||
|
CuSuite *suite = CuSuiteNew();
|
||||||
|
SUITE_ADD_TEST(suite, test_gamedata);
|
||||||
|
return suite;
|
||||||
|
}
|
|
@ -2,7 +2,9 @@
|
||||||
#include "password.h"
|
#include "password.h"
|
||||||
|
|
||||||
#include <md5.h>
|
#include <md5.h>
|
||||||
|
#include <crypt_blowfish.h>
|
||||||
#include <mtrand.h>
|
#include <mtrand.h>
|
||||||
|
#include <drepper.h>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -25,73 +27,104 @@
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
char *password_gensalt(void) {
|
char *password_gensalt(char *salt, size_t salt_len) {
|
||||||
static char salt[SALTLEN + 1];
|
size_t buflen = salt_len-1;
|
||||||
char *cp = salt;
|
char *cp = salt;
|
||||||
int buflen = SALTLEN;
|
|
||||||
while (buflen) {
|
while (buflen) {
|
||||||
unsigned long ul = genrand_int32() & (unsigned long)time(0);
|
unsigned long ul = genrand_int32() & (unsigned long)time(0);
|
||||||
b64_from_24bit((char)(ul & 0xFF), (char)((ul >> 8) & 0xff), (char)((ul >> 16) & 0xFF), 4);
|
b64_from_24bit((char)(ul & 0xFF), (char)((ul >> 8) & 0xff), (char)((ul >> 16) & 0xFF), 4);
|
||||||
}
|
}
|
||||||
salt[SALTLEN] = 0;
|
salt[salt_len-1] = 0;
|
||||||
return salt;
|
return salt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char * password_hash_i(const char * passwd, const char *salt, int algo, char *result, size_t len) {
|
static bool password_is_implemented(int algo) {
|
||||||
assert(passwd);
|
return algo == PASSWORD_PLAINTEXT || algo == PASSWORD_BCRYPT || algo == PASSWORD_NOCRYPT || algo == PASSWORD_MD5 || algo == PASSWORD_APACHE_MD5;
|
||||||
if (!salt) {
|
|
||||||
salt = password_gensalt();
|
|
||||||
}
|
}
|
||||||
if (algo==PASSWORD_PLAIN) {
|
|
||||||
_snprintf(result, len, "$0$%s$%s", salt, passwd);
|
static const char * password_hash_i(const char * passwd, const char *input, int algo, char *result, size_t len) {
|
||||||
|
if (algo == PASSWORD_BCRYPT) {
|
||||||
|
char salt[MAXSALTLEN];
|
||||||
|
char setting[40];
|
||||||
|
if (!input) {
|
||||||
|
input = password_gensalt(salt, MAXSALTLEN);
|
||||||
}
|
}
|
||||||
else if (algo == PASSWORD_MD5) {
|
if (_crypt_gensalt_blowfish_rn("$2y$", 5, input, strlen(input), setting, sizeof(setting)) == NULL) {
|
||||||
return md5_crypt_r(passwd, salt, result, len);
|
return NULL;
|
||||||
}
|
}
|
||||||
else if (algo == PASSWORD_APACHE_MD5) {
|
if (_crypt_blowfish_rn(passwd, setting, result, len) == NULL) {
|
||||||
apr_md5_encode(passwd, salt, result, len);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
else if (algo == PASSWORD_PLAINTEXT) {
|
||||||
const char * password_hash(const char * passwd, const char * salt, int algo) {
|
_snprintf(result, len, "%s", passwd);
|
||||||
static char result[64]; // TODO: static result buffers are bad mojo!
|
return result;
|
||||||
if (algo < 0) algo = PASSWORD_DEFAULT;
|
}
|
||||||
return password_hash_i(passwd, salt, algo, result, sizeof(result));
|
else if (algo == PASSWORD_NOCRYPT) {
|
||||||
|
_snprintf(result, len, "$0$%s", passwd);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else if (password_is_implemented(algo)) {
|
||||||
|
char salt[MAXSALTLEN];
|
||||||
|
assert(passwd);
|
||||||
|
if (input) {
|
||||||
|
const char * dol = strchr(input, '$');
|
||||||
|
size_t salt_len;
|
||||||
|
if (dol) {
|
||||||
|
assert(dol > input && dol[0] == '$');
|
||||||
|
salt_len = dol - input;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
salt_len = strlen(input);
|
||||||
|
}
|
||||||
|
assert(salt_len < MAXSALTLEN);
|
||||||
|
memcpy(salt, input, salt_len);
|
||||||
|
salt[salt_len] = 0;
|
||||||
|
} else {
|
||||||
|
input = password_gensalt(salt, sizeof(salt));
|
||||||
|
}
|
||||||
|
if (algo == PASSWORD_MD5) {
|
||||||
|
return md5_crypt_r(passwd, input, result, len);
|
||||||
|
}
|
||||||
|
else if (algo == PASSWORD_APACHE_MD5) {
|
||||||
|
apr_md5_encode(passwd, input, result, len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool password_is_implemented(int algo) {
|
const char * password_encode(const char * passwd, int algo) {
|
||||||
return algo==PASSWORD_PLAIN || algo==PASSWORD_MD5 || algo==PASSWORD_APACHE_MD5;
|
static char result[64]; // TODO: static result buffers are bad mojo!
|
||||||
|
if (algo < 0) algo = PASSWORD_DEFAULT;
|
||||||
|
return password_hash_i(passwd, 0, algo, result, sizeof(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
int password_verify(const char * pwhash, const char * passwd) {
|
int password_verify(const char * pwhash, const char * passwd) {
|
||||||
char salt[MAXSALTLEN+1];
|
|
||||||
char hash[64];
|
char hash[64];
|
||||||
size_t len;
|
int algo = PASSWORD_PLAINTEXT;
|
||||||
int algo;
|
|
||||||
char *pos;
|
char *pos;
|
||||||
const char *dol, *result;
|
const char *result;
|
||||||
assert(passwd);
|
assert(passwd);
|
||||||
assert(pwhash);
|
assert(pwhash);
|
||||||
assert(pwhash[0] == '$');
|
if (pwhash[0] == '$') {
|
||||||
algo = pwhash[1];
|
algo = pwhash[1];
|
||||||
pos = strchr(pwhash+2, '$');
|
}
|
||||||
assert(pos && pos[0] == '$');
|
|
||||||
++pos;
|
|
||||||
dol = strchr(pos, '$');
|
|
||||||
assert(dol>pos && dol[0] == '$');
|
|
||||||
len = dol - pos;
|
|
||||||
assert(len <= MAXSALTLEN);
|
|
||||||
strncpy(salt, pos, len);
|
|
||||||
salt[len] = 0;
|
|
||||||
result = password_hash_i(passwd, salt, algo, hash, sizeof(hash));
|
|
||||||
if (!password_is_implemented(algo)) {
|
if (!password_is_implemented(algo)) {
|
||||||
return VERIFY_UNKNOWN;
|
return VERIFY_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
if (algo == PASSWORD_PLAINTEXT) {
|
||||||
|
return (strcmp(passwd, pwhash) == 0) ? VERIFY_OK : VERIFY_FAIL;
|
||||||
|
} else if (algo == PASSWORD_BCRYPT) {
|
||||||
|
char sample[200];
|
||||||
|
_crypt_blowfish_rn(passwd, pwhash, sample, sizeof(sample));
|
||||||
|
return (strcmp(sample, pwhash) == 0) ? VERIFY_OK : VERIFY_FAIL;
|
||||||
|
}
|
||||||
|
pos = strchr(pwhash+2, '$');
|
||||||
|
assert(pos && pos[0] == '$');
|
||||||
|
pos = strchr(pos, '$')+1;
|
||||||
|
result = password_hash_i(passwd, pos, algo, hash, sizeof(hash));
|
||||||
if (strcmp(pwhash, result) == 0) {
|
if (strcmp(pwhash, result) == 0) {
|
||||||
return VERIFY_OK;
|
return VERIFY_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define PASSWORD_PLAIN '0'
|
#define PASSWORD_PLAINTEXT 0
|
||||||
|
#define PASSWORD_NOCRYPT '0'
|
||||||
#define PASSWORD_MD5 '1'
|
#define PASSWORD_MD5 '1'
|
||||||
#define PASSWORD_BCRYPT '2' // not implemented
|
#define PASSWORD_BCRYPT '2' // not implemented
|
||||||
#define PASSWORD_APACHE_MD5 'a'
|
#define PASSWORD_APACHE_MD5 'a'
|
||||||
#define PASSWORD_SHA256 '5' // not implemented
|
#define PASSWORD_SHA256 '5' // not implemented
|
||||||
#define PASSWORD_SHA512 '6' // not implemented
|
#define PASSWORD_SHA512 '6' // not implemented
|
||||||
#define PASSWORD_DEFAULT PASSWORD_APACHE_MD5
|
#define PASSWORD_DEFAULT PASSWORD_PLAINTEXT
|
||||||
|
|
||||||
#define VERIFY_OK 0 // password matches hash
|
#define VERIFY_OK 0 // password matches hash
|
||||||
#define VERIFY_FAIL 1 // password is wrong
|
#define VERIFY_FAIL 1 // password is wrong
|
||||||
#define VERIFY_UNKNOWN 2 // hashing algorithm not supported
|
#define VERIFY_UNKNOWN 2 // hashing algorithm not supported
|
||||||
int password_verify(const char *hash, const char *passwd);
|
int password_verify(const char *hash, const char *passwd);
|
||||||
const char * password_hash(const char *passwd, const char *salt, int algo);
|
const char * password_encode(const char *passwd, int algo);
|
||||||
|
|
|
@ -1,25 +1,43 @@
|
||||||
#include <platform.h>
|
#include <platform.h>
|
||||||
#include <CuTest.h>
|
|
||||||
#include "password.h"
|
#include "password.h"
|
||||||
|
#include <CuTest.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
static void test_passwords(CuTest *tc) {
|
static void test_passwords(CuTest *tc) {
|
||||||
const char *hash;
|
const char *hash, *expect;
|
||||||
|
|
||||||
hash = password_hash("Hodor", "FqQLkl8g", PASSWORD_APACHE_MD5);
|
expect = "$apr1$FqQLkl8g$.icQqaDJpim4BVy.Ho5660";
|
||||||
|
CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "Hodor"));
|
||||||
|
hash = password_encode("Hodor", PASSWORD_APACHE_MD5);
|
||||||
CuAssertPtrNotNull(tc, hash);
|
CuAssertPtrNotNull(tc, hash);
|
||||||
CuAssertStrEquals(tc, "$apr1$FqQLkl8g$.icQqaDJpim4BVy.Ho5660", hash);
|
CuAssertIntEquals(tc, 0, strncmp(hash, expect, 6));
|
||||||
CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "Hodor"));
|
|
||||||
|
|
||||||
hash = password_hash("jollygood", "ZouUn04i", PASSWORD_MD5);
|
expect = "$1$ZouUn04i$yNnT1Oy8azJ5V.UM9ppP5/";
|
||||||
|
CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "jollygood"));
|
||||||
|
hash = password_encode("jollygood", PASSWORD_MD5);
|
||||||
CuAssertPtrNotNull(tc, hash);
|
CuAssertPtrNotNull(tc, hash);
|
||||||
CuAssertStrEquals(tc, "$1$ZouUn04i$yNnT1Oy8azJ5V.UM9ppP5/", hash);
|
CuAssertIntEquals(tc, 0, strncmp(hash, expect, 3));
|
||||||
CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "jollygood"));
|
|
||||||
|
|
||||||
hash = password_hash("password", "hodor", PASSWORD_PLAIN);
|
expect = "password";
|
||||||
|
hash = password_encode("password", PASSWORD_PLAINTEXT);
|
||||||
CuAssertPtrNotNull(tc, hash);
|
CuAssertPtrNotNull(tc, hash);
|
||||||
CuAssertStrEquals(tc, "$0$hodor$password", hash);
|
CuAssertStrEquals(tc, hash, expect);
|
||||||
CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "password"));
|
CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "password"));
|
||||||
CuAssertIntEquals(tc, VERIFY_FAIL, password_verify(hash, "arseword"));
|
CuAssertIntEquals(tc, VERIFY_FAIL, password_verify(expect, "arseword"));
|
||||||
|
|
||||||
|
expect = "$0$password";
|
||||||
|
hash = password_encode("password", PASSWORD_NOCRYPT);
|
||||||
|
CuAssertPtrNotNull(tc, hash);
|
||||||
|
CuAssertStrEquals(tc, hash, expect);
|
||||||
|
CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "password"));
|
||||||
|
CuAssertIntEquals(tc, VERIFY_FAIL, password_verify(expect, "arseword"));
|
||||||
|
|
||||||
|
expect = "$2y$05$RJ8qAhu.foXyJLdc2eHTLOaK4MDYn3/v4HtOVCq0Plv2yxcrEB7Wm";
|
||||||
|
CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "Hodor"));
|
||||||
|
hash = password_encode("Hodor", PASSWORD_BCRYPT);
|
||||||
|
CuAssertPtrNotNull(tc, hash);
|
||||||
|
CuAssertIntEquals(tc, 0, strncmp(hash, expect, 7));
|
||||||
|
|
||||||
CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify("$9$saltyfish$password", "password"));
|
CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify("$9$saltyfish$password", "password"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ TESTS=../Debug/eressea/test_eressea
|
||||||
SERVER=../Debug/eressea/eressea
|
SERVER=../Debug/eressea/eressea
|
||||||
if [ -n "$VALGRIND" ]; then
|
if [ -n "$VALGRIND" ]; then
|
||||||
SUPP=../share/ubuntu-12_04.supp
|
SUPP=../share/ubuntu-12_04.supp
|
||||||
VALGRIND="$VALGRIND --suppressions=$SUPP --error-exitcode=1 --leak-check=no"
|
VALGRIND="$VALGRIND --track-origins=yes --gen-suppressions=all --suppressions=$SUPP --error-exitcode=1 --leak-check=no"
|
||||||
fi
|
fi
|
||||||
echo "running $TESTS"
|
echo "running $TESTS"
|
||||||
$VALGRIND $TESTS
|
$VALGRIND $TESTS
|
||||||
|
|
Loading…
Reference in a new issue