forked from github/server
Merge pull request #455 from ennorehling/feature/crypt-passwords
Kein feedback bekommen, Schade eigentlich. Dann werde ich das mal mergen und hoffen, dass es im preview funktioniert (3.8 kommt ja bald).
This commit is contained in:
commit
6a4bee28d0
2
crypto
2
crypto
|
@ -1 +1 @@
|
|||
Subproject commit 166fdc8c146755055217070c58079ba9a7c03369
|
||||
Subproject commit f9ecf5a10983adfc7bd1bee8ac1f9a3abf1d41d9
|
|
@ -1083,7 +1083,8 @@ function test_parser()
|
|||
|
||||
local file = io.open(filename, "w")
|
||||
assert_not_nil(file)
|
||||
file:write('ERESSEA ' .. itoa36(f.id) .. ' "' .. f.password .. '"\n')
|
||||
f.password = 'Hodor'
|
||||
file:write('ERESSEA ' .. itoa36(f.id) .. ' "Hodor"\n')
|
||||
file:write('EINHEIT ' .. itoa36(u.id) .. "\n")
|
||||
file:write("BENENNEN EINHEIT 'Goldene Herde'\n")
|
||||
file:close()
|
||||
|
|
|
@ -73,12 +73,10 @@ function test_process_settings()
|
|||
f.options = 0
|
||||
u:add_order("EMAIL herp@derp.com")
|
||||
u:add_order("BANNER 'Herpderp'")
|
||||
u:add_order("PASSWORT 'HerpDerp'")
|
||||
u:add_order("OPTION AUSWERTUNG")
|
||||
eressea.process.settings()
|
||||
assert_equal("herp@derp.com", f.email)
|
||||
assert_equal("Herpderp", f.info)
|
||||
assert_equal("HerpDerp", f.password)
|
||||
assert_equal(1, f.options)
|
||||
end
|
||||
|
||||
|
@ -98,7 +96,8 @@ end
|
|||
|
||||
function test_process_quit()
|
||||
fno = f.id
|
||||
u:add_order("STIRB '" .. u.faction.password .. "'")
|
||||
u.faction.password = 'Hodor'
|
||||
u:add_order("STIRB 'Hodor'")
|
||||
assert_not_equal(nil, _G.get_faction(fno))
|
||||
eressea.process.quit()
|
||||
eressea.write_game('test.dat')
|
||||
|
|
|
@ -35,3 +35,9 @@
|
|||
fun:__GI___strncasecmp_l
|
||||
fun:____strtod_l_internal
|
||||
}
|
||||
|
||||
{
|
||||
kde-bug-309427
|
||||
Memcheck:Cond
|
||||
fun:__stpncpy_sse2_unaligned
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ without prior permission by the authors of Eressea.
|
|||
|
||||
#include <util/language.h>
|
||||
#include <util/log.h>
|
||||
#include <util/password.h>
|
||||
|
||||
#include <quicklist.h>
|
||||
|
||||
#include <tolua.h>
|
||||
|
@ -382,15 +384,15 @@ static int tolua_faction_create(lua_State * L)
|
|||
|
||||
static int tolua_faction_get_password(lua_State * L)
|
||||
{
|
||||
faction *self = (faction *)tolua_tousertype(L, 1, 0);
|
||||
tolua_pushstring(L, faction_getpassword(self));
|
||||
return 1;
|
||||
unused_arg(L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tolua_faction_set_password(lua_State * L)
|
||||
{
|
||||
faction *self = (faction *)tolua_tousertype(L, 1, 0);
|
||||
faction_setpassword(self, tolua_tostring(L, 2, 0));
|
||||
const char * passw = tolua_tostring(L, 2, 0);
|
||||
faction_setpassword(self, password_hash(passw, 0, PASSWORD_DEFAULT));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include <platform.h>
|
||||
#include <kernel/config.h>
|
||||
#include "faction.h"
|
||||
|
||||
#include "alliance.h"
|
||||
#include "ally.h"
|
||||
#include "curse.h"
|
||||
|
@ -46,6 +45,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include <util/language.h>
|
||||
#include <util/log.h>
|
||||
#include <util/parser.h>
|
||||
#include <util/password.h>
|
||||
#include <util/resolve.h>
|
||||
#include <util/rng.h>
|
||||
#include <util/variant.h>
|
||||
|
@ -104,7 +104,7 @@ static void free_faction(faction * f)
|
|||
|
||||
free(f->email);
|
||||
free(f->banner);
|
||||
free(f->passw);
|
||||
free(f->_password);
|
||||
free(f->name);
|
||||
if (f->seen_factions) {
|
||||
ql_free(f->seen_factions);
|
||||
|
@ -251,7 +251,9 @@ faction *addfaction(const char *email, const char *password,
|
|||
log_warning("Invalid email address for faction %s: %s\n", itoa36(f->no), email);
|
||||
}
|
||||
|
||||
faction_setpassword(f, password);
|
||||
if (!password) password = itoa36(rng_int());
|
||||
faction_setpassword(f, password_hash(password, 0, PASSWORD_DEFAULT));
|
||||
ADDMSG(&f->msgs, msg_message("changepasswd", "value", password));
|
||||
|
||||
f->alliance_joindate = turn;
|
||||
f->lastorders = turn;
|
||||
|
@ -313,7 +315,13 @@ unit *addplayer(region * r, faction * f)
|
|||
|
||||
bool checkpasswd(const faction * f, const char *passwd)
|
||||
{
|
||||
return (passwd && unicode_utf8_strcasecmp(f->passw, passwd) == 0);
|
||||
if (!passwd) return false;
|
||||
|
||||
if (password_verify(f->_password, passwd) == VERIFY_FAIL) {
|
||||
log_warning("password check failed: %s", factionname(f));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
variant read_faction_reference(struct storage * store)
|
||||
|
@ -556,13 +564,11 @@ void faction_setbanner(faction * self, const char *banner)
|
|||
self->banner = _strdup(banner);
|
||||
}
|
||||
|
||||
void faction_setpassword(faction * f, const char *passw)
|
||||
void faction_setpassword(faction * f, const char *pwhash)
|
||||
{
|
||||
free(f->passw);
|
||||
if (passw)
|
||||
f->passw = _strdup(passw);
|
||||
else
|
||||
f->passw = _strdup(itoa36(rng_int()));
|
||||
assert(pwhash && pwhash[0] == '$');
|
||||
free(f->_password);
|
||||
f->_password = _strdup(pwhash);
|
||||
}
|
||||
|
||||
bool valid_race(const struct faction *f, const struct race *rc)
|
||||
|
@ -577,11 +583,6 @@ bool valid_race(const struct faction *f, const struct race *rc)
|
|||
}
|
||||
}
|
||||
|
||||
const char *faction_getpassword(const faction * f)
|
||||
{
|
||||
return f->passw;
|
||||
}
|
||||
|
||||
struct alliance *f_get_alliance(const struct faction *f)
|
||||
{
|
||||
if (f->alliance && !(f->alliance->flags & ALF_NON_ALLIED)) {
|
||||
|
@ -846,3 +847,28 @@ faction *dfindhash(int no)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int writepasswd(void)
|
||||
{
|
||||
FILE *F;
|
||||
char zText[128];
|
||||
|
||||
sprintf(zText, "%s/passwd", basepath());
|
||||
F = fopen(zText, "w");
|
||||
if (!F) {
|
||||
perror(zText);
|
||||
}
|
||||
else {
|
||||
faction *f;
|
||||
log_info("writing passwords...");
|
||||
|
||||
for (f = factions; f; f = f->next) {
|
||||
fprintf(F, "%s:%s:%s:%u\n",
|
||||
factionid(f), f->email, f->_password, f->subscription);
|
||||
}
|
||||
fclose(F);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ extern "C" {
|
|||
char *name;
|
||||
char *banner;
|
||||
char *email;
|
||||
char *passw;
|
||||
char *_password;
|
||||
int max_spelllevel;
|
||||
struct spellbook *spellbook;
|
||||
const struct locale *locale;
|
||||
|
@ -121,6 +121,7 @@ extern "C" {
|
|||
struct faction *addfaction(const char *email, const char *password,
|
||||
const struct race *frace, const struct locale *loc, int subscription);
|
||||
bool checkpasswd(const faction * f, const char *passwd);
|
||||
int writepasswd(void);
|
||||
void destroyfaction(faction ** f);
|
||||
|
||||
bool faction_alive(const struct faction *f);
|
||||
|
@ -152,8 +153,7 @@ extern "C" {
|
|||
const char *faction_getemail(const struct faction *self);
|
||||
void faction_setemail(struct faction *self, const char *email);
|
||||
|
||||
const char *faction_getpassword(const struct faction *self);
|
||||
void faction_setpassword(struct faction *self, const char *password);
|
||||
void faction_setpassword(struct faction *self, const char *pwhash);
|
||||
bool valid_race(const struct faction *f, const struct race *rc);
|
||||
|
||||
void faction_getorigin(const struct faction * f, int id, int *x, int *y);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <kernel/plane.h>
|
||||
#include <kernel/config.h>
|
||||
#include <util/language.h>
|
||||
#include <util/password.h>
|
||||
|
||||
#include "monster.h"
|
||||
#include <CuTest.h>
|
||||
|
@ -107,7 +108,7 @@ static void test_addfaction(CuTest *tc) {
|
|||
CuAssertPtrEquals(tc, NULL, (void *)f->ursprung);
|
||||
CuAssertPtrEquals(tc, (void *)factions, (void *)f);
|
||||
CuAssertStrEquals(tc, "test@eressea.de", f->email);
|
||||
CuAssertStrEquals(tc, "hurrdurr", f->passw);
|
||||
CuAssertIntEquals(tc, true, checkpasswd(f, "hurrdurr"));
|
||||
CuAssertPtrEquals(tc, (void *)lang, (void *)f->locale);
|
||||
CuAssertIntEquals(tc, 1234, f->subscription);
|
||||
CuAssertIntEquals(tc, 0, f->flags);
|
||||
|
@ -119,6 +120,16 @@ static void test_addfaction(CuTest *tc) {
|
|||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_check_passwd(CuTest *tc) {
|
||||
faction *f;
|
||||
|
||||
f = test_create_faction(0);
|
||||
faction_setpassword(f, password_hash("password", 0, PASSWORD_DEFAULT));
|
||||
CuAssertIntEquals(tc, true, checkpasswd(f, "password"));
|
||||
CuAssertIntEquals(tc, false, checkpasswd(f, "assword"));
|
||||
CuAssertIntEquals(tc, false, checkpasswd(f, "PASSWORD"));
|
||||
}
|
||||
|
||||
static void test_get_monsters(CuTest *tc) {
|
||||
faction *f;
|
||||
|
||||
|
@ -185,5 +196,6 @@ CuSuite *get_faction_suite(void)
|
|||
SUITE_ADD_TEST(suite, test_get_monsters);
|
||||
SUITE_ADD_TEST(suite, test_set_origin);
|
||||
SUITE_ADD_TEST(suite, test_set_origin_bug);
|
||||
SUITE_ADD_TEST(suite, test_check_passwd);
|
||||
return suite;
|
||||
}
|
||||
|
|
|
@ -64,13 +64,14 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include <util/lists.h>
|
||||
#include <util/log.h>
|
||||
#include <util/parser.h>
|
||||
#include <quicklist.h>
|
||||
#include <util/password.h>
|
||||
#include <util/rand.h>
|
||||
#include <util/resolve.h>
|
||||
#include <util/rng.h>
|
||||
#include <util/umlaut.h>
|
||||
#include <util/unicode.h>
|
||||
|
||||
#include <quicklist.h>
|
||||
#include <stream.h>
|
||||
#include <filestream.h>
|
||||
#include <storage.h>
|
||||
|
@ -1216,7 +1217,7 @@ faction *readfaction(struct gamedata * data)
|
|||
}
|
||||
|
||||
READ_STR(data->store, name, sizeof(name));
|
||||
f->passw = _strdup(name);
|
||||
faction_setpassword(f, (data->version >= CRYPT_VERSION) ? name : password_hash(name, 0, PASSWORD_DEFAULT));
|
||||
if (data->version < NOOVERRIDE_VERSION) {
|
||||
READ_STR(data->store, 0, 0);
|
||||
}
|
||||
|
@ -1320,10 +1321,10 @@ void writefaction(struct gamedata *data, const faction * f)
|
|||
}
|
||||
WRITE_INT(data->store, f->alliance_joindate);
|
||||
|
||||
WRITE_STR(data->store, (const char *)f->name);
|
||||
WRITE_STR(data->store, (const char *)f->banner);
|
||||
WRITE_STR(data->store, f->name);
|
||||
WRITE_STR(data->store, f->banner);
|
||||
WRITE_STR(data->store, f->email);
|
||||
WRITE_TOK(data->store, (const char *)f->passw);
|
||||
WRITE_TOK(data->store, f->_password);
|
||||
WRITE_TOK(data->store, locale_name(f->locale));
|
||||
WRITE_INT(data->store, f->lastorders);
|
||||
WRITE_INT(data->store, f->age);
|
||||
|
|
|
@ -33,8 +33,8 @@
|
|||
#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 ATTRIBOWNER_VERSION 350 /* all attrib_type functions know who owns the attribute */
|
||||
|
||||
#define RELEASE_VERSION ATTRIBOWNER_VERSION /* current datafile */
|
||||
#define CRYPT_VERSION 351 /* passwords are encrypted */
|
||||
#define RELEASE_VERSION CRYPT_VERSION /* current datafile */
|
||||
#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 */
|
||||
|
||||
|
|
64
src/laws.c
64
src/laws.c
|
@ -79,6 +79,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include <util/lists.h>
|
||||
#include <util/log.h>
|
||||
#include <util/parser.h>
|
||||
#include <util/password.h>
|
||||
#include <quicklist.h>
|
||||
#include <util/rand.h>
|
||||
#include <util/rng.h>
|
||||
|
@ -733,9 +734,6 @@ void nmr_warnings(void)
|
|||
faction *f, *fa;
|
||||
#define FRIEND (HELP_GUARD|HELP_MONEY)
|
||||
for (f = factions; f; f = f->next) {
|
||||
if (f->age <= 1) {
|
||||
ADDMSG(&f->msgs, msg_message("changepasswd", "value", f->passw));
|
||||
}
|
||||
if (!fval(f, FFL_NOIDLEOUT) && turn > f->lastorders) {
|
||||
ADDMSG(&f->msgs, msg_message("nmr_warning", ""));
|
||||
if (turn - f->lastorders == NMRTimeout() - 1) {
|
||||
|
@ -2168,16 +2166,13 @@ int password_cmd(unit * u, struct order *ord)
|
|||
}
|
||||
}
|
||||
}
|
||||
free(u->faction->passw);
|
||||
if (!pwok) {
|
||||
cmistake(u, ord, 283, MSG_EVENT);
|
||||
u->faction->passw = _strdup(itoa36(rng_int()));
|
||||
}
|
||||
else {
|
||||
u->faction->passw = _strdup(pwbuf);
|
||||
strlcpy(pwbuf, itoa36(rng_int()), sizeof(pwbuf));
|
||||
}
|
||||
faction_setpassword(u->faction, password_hash(pwbuf, 0, PASSWORD_DEFAULT));
|
||||
ADDMSG(&u->faction->msgs, msg_message("changepasswd",
|
||||
"value", u->faction->passw));
|
||||
"value", pwbuf));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4256,32 +4251,6 @@ static void maintain_buildings_1(region * r)
|
|||
maintain_buildings(r, false);
|
||||
}
|
||||
|
||||
/** warn about passwords that are not US ASCII.
|
||||
* even though passwords are technically UTF8 strings, the server receives
|
||||
* them as part of the Subject of an email when reports are requested.
|
||||
* This means that we need to limit them to ASCII characters until that
|
||||
* mechanism has been changed.
|
||||
*/
|
||||
static int warn_password(void)
|
||||
{
|
||||
faction *f;
|
||||
for (f = factions; f; f = f->next) {
|
||||
bool pwok = true;
|
||||
const char *c = f->passw;
|
||||
while (*c && pwok) {
|
||||
if (!isalnum((unsigned char)*c))
|
||||
pwok = false;
|
||||
c++;
|
||||
}
|
||||
if (!pwok) {
|
||||
free(f->passw);
|
||||
f->passw = _strdup(itoa36(rng_int()));
|
||||
ADDMSG(&f->msgs, msg_message("illegal_password", "newpass", f->passw));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void init_processor(void)
|
||||
{
|
||||
int p;
|
||||
|
@ -4463,31 +4432,6 @@ void processorders(void)
|
|||
/* immer ausführen, wenn neue Sprüche dazugekommen sind, oder sich
|
||||
* Beschreibungen geändert haben */
|
||||
update_spells();
|
||||
warn_password();
|
||||
}
|
||||
|
||||
int writepasswd(void)
|
||||
{
|
||||
FILE *F;
|
||||
char zText[128];
|
||||
|
||||
sprintf(zText, "%s/passwd", basepath());
|
||||
F = fopen(zText, "w");
|
||||
if (!F) {
|
||||
perror(zText);
|
||||
}
|
||||
else {
|
||||
faction *f;
|
||||
log_info("writing passwords...");
|
||||
|
||||
for (f = factions; f; f = f->next) {
|
||||
fprintf(F, "%s:%s:%s:%u\n",
|
||||
factionid(f), f->email, f->passw, f->subscription);
|
||||
}
|
||||
fclose(F);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void update_subscriptions(void)
|
||||
|
|
|
@ -38,7 +38,6 @@ extern "C" {
|
|||
extern int dropouts[2];
|
||||
extern int *age;
|
||||
|
||||
int writepasswd(void);
|
||||
void demographics(void);
|
||||
void immigration(void);
|
||||
void update_guards(void);
|
||||
|
|
|
@ -1061,6 +1061,7 @@ static void test_ally_cmd(CuTest *tc) {
|
|||
test_cleanup();
|
||||
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
|
||||
f = test_create_faction(0);
|
||||
|
||||
u->faction->locale = lang = get_or_create_locale("de");
|
||||
locale_setstring(lang, parameters[P_NOT], "NICHT");
|
||||
locale_setstring(lang, parameters[P_GUARD], "BEWACHE");
|
||||
|
@ -1105,8 +1106,9 @@ static void test_nmr_warnings(CuTest *tc) {
|
|||
CuAssertIntEquals(tc, 0, f1->age);
|
||||
nmr_warnings();
|
||||
CuAssertPtrNotNull(tc, f1->msgs);
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(f1->msgs, "changepasswd"));
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(f1->msgs, "nmr_warning"));
|
||||
CuAssertPtrNotNull(tc, f2->msgs);
|
||||
CuAssertPtrNotNull(tc, f2->msgs->begin);
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(f2->msgs, "nmr_warning"));
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(f2->msgs, "nmr_warning_final"));
|
||||
test_cleanup();
|
||||
|
|
|
@ -1427,7 +1427,7 @@ report_template(const char *filename, report_context * ctx, const char *charset)
|
|||
newline(out);
|
||||
newline(out);
|
||||
|
||||
sprintf(buf, "%s %s \"%s\"", LOC(f->locale, "ERESSEA"), factionid(f), f->passw);
|
||||
sprintf(buf, "%s %s \"password\"", LOC(f->locale, "ERESSEA"), factionid(f));
|
||||
rps_nowrap(out, buf);
|
||||
newline(out);
|
||||
newline(out);
|
||||
|
@ -2112,9 +2112,6 @@ const char *charset)
|
|||
|
||||
if (f->age <= 2) {
|
||||
const char *s;
|
||||
RENDER(f, buf, sizeof(buf), ("newbie_password", "password", f->passw));
|
||||
newline(out);
|
||||
centre(out, buf, true);
|
||||
s = locale_getstring(f->locale, "newbie_info_1");
|
||||
if (s) {
|
||||
newline(out);
|
||||
|
|
|
@ -45,6 +45,7 @@ static void setup_study(study_fixture *fix, skill_t sk) {
|
|||
fix->teachers[1] = test_create_unit(f, r);
|
||||
assert(fix->teachers[1]);
|
||||
fix->teachers[1]->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(fix->u->no));
|
||||
test_clear_messages(f);
|
||||
}
|
||||
|
||||
static void test_study_no_teacher(CuTest *tc) {
|
||||
|
|
|
@ -80,6 +80,7 @@ int RunAllTests(int argc, char *argv[])
|
|||
ADD_SUITE(bsdstring);
|
||||
ADD_SUITE(functions);
|
||||
ADD_SUITE(parser);
|
||||
ADD_SUITE(password);
|
||||
ADD_SUITE(umlaut);
|
||||
ADD_SUITE(unicode);
|
||||
ADD_SUITE(strings);
|
||||
|
|
|
@ -64,6 +64,7 @@ struct region *test_create_region(int x, int y, const terrain_type *terrain)
|
|||
struct faction *test_create_faction(const struct race *rc)
|
||||
{
|
||||
faction *f = addfaction("nobody@eressea.de", NULL, rc ? rc : test_create_race("human"), default_locale, 0);
|
||||
test_clear_messages(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ project(util C)
|
|||
SET(_TEST_FILES
|
||||
base36.test.c
|
||||
parser.test.c
|
||||
password.test.c
|
||||
attrib.test.c
|
||||
strings.test.c
|
||||
bsdstring.test.c
|
||||
|
@ -28,6 +29,7 @@ log.c
|
|||
message.c
|
||||
nrmessage.c
|
||||
parser.c
|
||||
password.c
|
||||
rand.c
|
||||
resolve.c
|
||||
strings.c
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
#include <platform.h>
|
||||
#include "password.h"
|
||||
|
||||
#include <md5.h>
|
||||
#include <mtrand.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#define MAXSALTLEN 32 // maximum length in characters of any salt
|
||||
#define SALTLEN 8 // length of salts we generate
|
||||
|
||||
/* Table with characters for base64 transformation. */
|
||||
static const char b64t[65] =
|
||||
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
#define b64_from_24bit(B2, B1, B0, N) \
|
||||
do { \
|
||||
unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \
|
||||
int n = (N); \
|
||||
while (n-- > 0 && buflen > 0) \
|
||||
{ \
|
||||
*cp++ = b64t[w & 0x3f]; \
|
||||
--buflen; \
|
||||
w >>= 6; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
char *password_gensalt(void) {
|
||||
static char salt[SALTLEN + 1];
|
||||
char *cp = salt;
|
||||
int buflen = SALTLEN;
|
||||
while (buflen) {
|
||||
unsigned long ul = genrand_int32() & (unsigned long)time(0);
|
||||
b64_from_24bit((char)(ul & 0xFF), (char)((ul>>8)&0xff), (char)((ul>>16)&0xFF), 4);
|
||||
}
|
||||
salt[SALTLEN] = 0;
|
||||
return salt;
|
||||
}
|
||||
|
||||
static const char * password_hash_i(const char * passwd, const char *salt, int algo, char *result, size_t len) {
|
||||
assert(passwd);
|
||||
if (!salt) {
|
||||
salt = password_gensalt();
|
||||
}
|
||||
if (algo==PASSWORD_PLAIN) {
|
||||
_snprintf(result, len, "$0$%s$%s", salt, passwd);
|
||||
}
|
||||
else if (algo == PASSWORD_MD5) {
|
||||
char * result = md5_crypt(passwd, salt);
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const char * password_hash(const char * passwd, const char * salt, int algo) {
|
||||
static char result[64]; // TODO: static result buffers are bad mojo!
|
||||
if (algo < 0) algo = PASSWORD_DEFAULT;
|
||||
return password_hash_i(passwd, salt, algo, result, sizeof(result));
|
||||
}
|
||||
|
||||
static bool password_is_implemented(int algo) {
|
||||
return algo==PASSWORD_PLAIN || algo==PASSWORD_MD5;
|
||||
}
|
||||
|
||||
int password_verify(const char * pwhash, const char * passwd) {
|
||||
char salt[MAXSALTLEN+1];
|
||||
char hash[64];
|
||||
size_t len;
|
||||
int algo;
|
||||
char *pos;
|
||||
const char *dol, *result;
|
||||
assert(passwd);
|
||||
assert(pwhash);
|
||||
assert(pwhash[0] == '$');
|
||||
algo = pwhash[1] - '0';
|
||||
pos = strchr(pwhash+2, '$');
|
||||
assert(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)) {
|
||||
return VERIFY_UNKNOWN;
|
||||
}
|
||||
if (strcmp(pwhash, result) == 0) {
|
||||
return VERIFY_OK;
|
||||
}
|
||||
return VERIFY_FAIL;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#define PASSWORD_PLAIN 0
|
||||
#define PASSWORD_MD5 1
|
||||
#define PASSWORD_BCRYPT 2 // not implemented
|
||||
#define PASSWORD_SHA256 5 // not implemented
|
||||
#define PASSWORD_SHA512 6 // not implemented
|
||||
#define PASSWORD_DEFAULT PASSWORD_MD5
|
||||
|
||||
|
||||
#define VERIFY_OK 0 // password matches hash
|
||||
#define VERIFY_FAIL 1 // password is wrong
|
||||
#define VERIFY_UNKNOWN 2 // hashing algorithm not supported
|
||||
int password_verify(const char *hash, const char *passwd);
|
||||
const char * password_hash(const char *passwd, const char *salt, int algo);
|
|
@ -0,0 +1,25 @@
|
|||
#include <platform.h>
|
||||
#include <CuTest.h>
|
||||
#include "password.h"
|
||||
|
||||
static void test_passwords(CuTest *tc) {
|
||||
const char *hash;
|
||||
|
||||
hash = password_hash("jollygood", "ZouUn04i", PASSWORD_MD5);
|
||||
CuAssertPtrNotNull(tc, hash);
|
||||
CuAssertStrEquals(tc, "$1$ZouUn04i$yNnT1Oy8azJ5V.UM9ppP5/", hash);
|
||||
CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "jollygood"));
|
||||
|
||||
hash = password_hash("password", "hodor", PASSWORD_PLAIN);
|
||||
CuAssertPtrNotNull(tc, hash);
|
||||
CuAssertStrEquals(tc, "$0$hodor$password", hash);
|
||||
CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "password"));
|
||||
CuAssertIntEquals(tc, VERIFY_FAIL, password_verify(hash, "arseword"));
|
||||
CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify("$9$saltyfish$password", "password"));
|
||||
}
|
||||
|
||||
CuSuite *get_password_suite(void) {
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_passwords);
|
||||
return suite;
|
||||
}
|
Loading…
Reference in New Issue