diff --git a/crypto b/crypto index e0f9891a9..93dc9200f 160000 --- a/crypto +++ b/crypto @@ -1 +1 @@ -Subproject commit e0f9891a91d69c042f82c1e13e48ab4c7160381d +Subproject commit 93dc9200fa4cb6bfa3883b19f6d33fd416ca43da diff --git a/share/debian-7_8.supp b/share/debian-7_8.supp index 11370ba26..99eefafa4 100644 --- a/share/debian-7_8.supp +++ b/share/debian-7_8.supp @@ -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 Memcheck:Cond diff --git a/share/ubuntu-12_04.supp b/share/ubuntu-12_04.supp index 504e4ae2d..bab3287ef 100644 --- a/share/ubuntu-12_04.supp +++ b/share/ubuntu-12_04.supp @@ -1,3 +1,8 @@ +{ + stpncpy in strcpy-sse2-unaligned.S:1659 + Memcheck:Value8 + fun:__stpncpy_sse2_unaligned +} # old zlib version { zlib1g-dev-1:1.2.3.4.dfsg diff --git a/src/battle.c b/src/battle.c index 65b2cff7b..8fb2ec252 100644 --- a/src/battle.c +++ b/src/battle.c @@ -228,21 +228,11 @@ static void message_faction(battle * b, faction * f, struct message *m) void message_all(battle * b, message * m) { bfaction *bf; - plane *p = rplane(b->region); - watcher *w; for (bf = b->factions; bf; bf = bf->next) { assert(bf->faction); message_faction(b, bf->faction, m); } - if (p) - for (w = p->watchers; w; w = w->next) { - for (bf = b->factions; bf; bf = bf->next) - if (bf->faction == w->faction) - break; - if (bf == NULL) - message_faction(b, w->faction, m); - } } static void fbattlerecord(battle * b, faction * f, const char *s) diff --git a/src/bind_faction.c b/src/bind_faction.c index 03a75281a..c6b747416 100644 --- a/src/bind_faction.c +++ b/src/bind_faction.c @@ -392,7 +392,7 @@ static int tolua_faction_set_password(lua_State * L) { faction *self = (faction *)tolua_tousertype(L, 1, 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; } diff --git a/src/bind_unit.c b/src/bind_unit.c index ed8eb74c3..e0b076710 100755 --- a/src/bind_unit.c +++ b/src/bind_unit.c @@ -153,8 +153,8 @@ static int tolua_unit_get_group(lua_State * L) static int tolua_unit_set_group(lua_State * L) { unit *self = (unit *)tolua_tousertype(L, 1, 0); - int result = join_group(self, tolua_tostring(L, 2, 0)); - lua_pushinteger(L, result); + group *g = join_group(self, tolua_tostring(L, 2, 0)); + lua_pushboolean(L, g!=NULL); return 1; } diff --git a/src/buildno.h b/src/buildno.h index 9727f88eb..3ce9dbfe7 100644 --- a/src/buildno.h +++ b/src/buildno.h @@ -1,3 +1,3 @@ #define VERSION_MAJOR 3 #define VERSION_MINOR 8 -#define VERSION_BUILD 3 +#define VERSION_BUILD 4 diff --git a/src/kernel/alliance.test.c b/src/kernel/alliance.test.c index 98e60e943..1b708b017 100644 --- a/src/kernel/alliance.test.c +++ b/src/kernel/alliance.test.c @@ -4,6 +4,7 @@ #include "alliance.h" #include #include +#include #include @@ -61,9 +62,31 @@ static void test_alliance_join(CuTest *tc) { test_cleanup(); } +static void test_alliance_dead_faction(CuTest *tc) { + faction *f, *f2; + alliance *al; + + test_cleanup(); + f = test_create_faction(0); + f2 = test_create_faction(0); + al = makealliance(42, "Hodor"); + setalliance(f, al); + setalliance(f2, al); + CuAssertPtrEquals(tc, f, alliance_get_leader(al)); + CuAssertIntEquals(tc, 2, ql_length(al->members)); + CuAssertPtrEquals(tc, al, f->alliance); + destroyfaction(&factions); + CuAssertIntEquals(tc, 1, ql_length(al->members)); + CuAssertPtrEquals(tc, f2, alliance_get_leader(al)); + CuAssertPtrEquals(tc, NULL, f->alliance); + CuAssertTrue(tc, !f->_alive); + test_cleanup(); +} + CuSuite *get_alliance_suite(void) { CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_alliance_dead_faction); SUITE_ADD_TEST(suite, test_alliance_make); SUITE_ADD_TEST(suite, test_alliance_join); return suite; diff --git a/src/kernel/faction.c b/src/kernel/faction.c index ac1cf69af..b52879464 100755 --- a/src/kernel/faction.c +++ b/src/kernel/faction.c @@ -252,7 +252,7 @@ faction *addfaction(const char *email, const char *password, } 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)); f->alliance_joindate = turn; @@ -567,7 +567,8 @@ void faction_setbanner(faction * self, const char *banner) void faction_setpassword(faction * f, const char *pwhash) { - assert(pwhash && pwhash[0] == '$'); + assert(pwhash); + // && pwhash[0] == '$'); free(f->_password); f->_password = _strdup(pwhash); } diff --git a/src/kernel/faction.test.c b/src/kernel/faction.test.c index 678aaa068..19b4da883 100644 --- a/src/kernel/faction.test.c +++ b/src/kernel/faction.test.c @@ -108,12 +108,12 @@ 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); - CuAssertIntEquals(tc, true, checkpasswd(f, "hurrdurr")); + CuAssertTrue(tc, checkpasswd(f, "hurrdurr")); CuAssertPtrEquals(tc, (void *)lang, (void *)f->locale); CuAssertIntEquals(tc, 1234, f->subscription); CuAssertIntEquals(tc, 0, f->flags); CuAssertIntEquals(tc, 0, f->age); - CuAssertIntEquals(tc, true, faction_alive(f)); + CuAssertTrue(tc, faction_alive(f)); CuAssertIntEquals(tc, M_GRAY, f->magiegebiet); CuAssertIntEquals(tc, turn, f->lastorders); CuAssertPtrEquals(tc, f, findfaction(f->no)); @@ -124,10 +124,10 @@ 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")); + faction_setpassword(f, password_encode("password", PASSWORD_DEFAULT)); + CuAssertTrue(tc, checkpasswd(f, "password")); + CuAssertTrue(tc, !checkpasswd(f, "assword")); + CuAssertTrue(tc, !checkpasswd(f, "PASSWORD")); } static void test_get_monsters(CuTest *tc) { diff --git a/src/kernel/group.c b/src/kernel/group.c index 9b8b368ad..fdef2b8b8 100755 --- a/src/kernel/group.c +++ b/src/kernel/group.c @@ -179,7 +179,7 @@ void set_group(struct unit *u, struct group *g) } } -bool join_group(unit * u, const char *name) +group *join_group(unit * u, const char *name) { group *g = NULL; @@ -192,7 +192,7 @@ bool join_group(unit * u, const char *name) } set_group(u, g); - return true; + return g; } void write_groups(struct storage *store, const faction * f) diff --git a/src/kernel/group.h b/src/kernel/group.h index b32bed4c1..145881c00 100755 --- a/src/kernel/group.h +++ b/src/kernel/group.h @@ -36,7 +36,7 @@ extern "C" { } group; extern struct attrib_type at_group; /* attribute for units assigned to a group */ - extern bool join_group(struct unit *u, const char *name); + extern struct group *join_group(struct unit *u, const char *name); extern void set_group(struct unit *u, struct group *g); extern struct group * get_group(const struct unit *u); extern void free_group(struct group *g); diff --git a/src/kernel/group.test.c b/src/kernel/group.test.c index fa3487cb3..7daca2829 100644 --- a/src/kernel/group.test.c +++ b/src/kernel/group.test.c @@ -83,8 +83,8 @@ static void test_group(CuTest * tc) assert(r && f); u = test_create_unit(f, r); assert(u); - CuAssertTrue(tc, join_group(u, "hodor")); - CuAssertPtrNotNull(tc, (g = get_group(u))); + CuAssertPtrNotNull(tc, (g = join_group(u, "hodor"))); + CuAssertPtrEquals(tc, g, get_group(u)); CuAssertStrEquals(tc, "hodor", g->name); CuAssertIntEquals(tc, 1, g->members); set_group(u, 0); diff --git a/src/kernel/plane.c b/src/kernel/plane.c index 32c95f6a8..2542a053d 100644 --- a/src/kernel/plane.c +++ b/src/kernel/plane.c @@ -290,14 +290,3 @@ int read_plane_reference(plane ** pp, struct storage *store) ur_add(id, pp, resolve_plane); return AT_READ_OK; } - -bool is_watcher(const struct plane * p, const struct faction * f) -{ - struct watcher *w; - if (!p) - return false; - w = p->watchers; - while (w && w->faction != f) - w = w->next; - return (w != NULL); -} diff --git a/src/kernel/plane.h b/src/kernel/plane.h index 2426c6a1a..d8ec176a9 100644 --- a/src/kernel/plane.h +++ b/src/kernel/plane.h @@ -23,6 +23,7 @@ extern "C" { #endif struct region; + struct faction; struct plane; struct storage; @@ -43,15 +44,8 @@ extern "C" { #define PFL_NOMONSTERS 16384 /* no monster randenc */ #define PFL_SEESPECIAL 32768 /* far seeing */ - typedef struct watcher { - struct watcher *next; - struct faction *faction; - unsigned char mode; - } watcher; - typedef struct plane { struct plane *next; - struct watcher *watchers; int id; char *name; int minx, maxx, miny, maxy; @@ -76,7 +70,6 @@ extern "C" { struct plane *get_homeplane(void); extern int rel_to_abs(const struct plane *pl, const struct faction *f, int rel, unsigned char index); - extern bool is_watcher(const struct plane *p, const struct faction *f); extern void write_plane_reference(const plane * p, struct storage *store); extern int read_plane_reference(plane ** pp, struct storage *store); extern int plane_width(const plane * pl); diff --git a/src/kernel/save.c b/src/kernel/save.c index 93bd8ffc0..1619b3622 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -544,7 +544,8 @@ static void write_owner(struct gamedata *data, region_owner * owner) write_faction_reference((f && f->_alive) ? f : NULL, data->store); // TODO: check that destroyfaction does the right thing. // TODO: What happens to morale when the owner dies? - write_faction_reference(owner->owner, data->store); + f = owner->owner; + write_faction_reference((f && f->_alive) ? f : NULL, data->store); } else { WRITE_INT(data->store, -1); @@ -766,6 +767,7 @@ void write_unit(struct gamedata *data, const unit * u) const race *irace = u_irace(u); write_unit_reference(u, data->store); + assert(u->faction->_alive); write_faction_reference(u->faction, data->store); WRITE_STR(data->store, u->_name); WRITE_STR(data->store, u->display ? u->display : ""); @@ -1159,6 +1161,58 @@ void write_spellbook(const struct spellbook *book, struct storage *store) 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. * This function requires no context, can be called in any state. The * faction may not already exist, however. @@ -1225,8 +1279,7 @@ faction *readfaction(struct gamedata * data) set_email(&f->email, ""); } - READ_STR(data->store, name, sizeof(name)); - faction_setpassword(f, (data->version > CRYPT_VERSION) ? name : password_hash(name, 0, PASSWORD_DEFAULT)); + read_password(data, f); if (data->version < NOOVERRIDE_VERSION) { READ_STR(data->store, 0, 0); } @@ -1313,6 +1366,7 @@ void writefaction(struct gamedata *data, const faction * f) ally *sf; ursprung *ur; + assert(f->_alive); write_faction_reference(f, data->store); WRITE_INT(data->store, f->subscription); #if RELEASE_VERSION >= SPELL_LEVEL_VERSION @@ -1333,7 +1387,7 @@ void writefaction(struct gamedata *data, const faction * f) WRITE_STR(data->store, f->name); WRITE_STR(data->store, f->banner); 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_INT(data->store, f->lastorders); WRITE_INT(data->store, f->age); @@ -1384,32 +1438,6 @@ static int cb_sb_maxlevel(spellbook_entry *sbe, void *cbdata) { return 0; } -void write_watchers(storage *store, plane *pl) { - watcher *w = pl->watchers; - while (w) { - if (w->faction && w->faction->_alive) { - write_faction_reference(w->faction, store); - WRITE_INT(store, w->mode); - } - w = w->next; - } - write_faction_reference(NULL, store); /* mark the end of the list */ -} - -void read_watchers(storage *store, plane *pl) { - variant fno = read_faction_reference(store); - while (fno.i) { - int n; - watcher *w = (watcher *)malloc(sizeof(watcher)); - ur_add(fno, &w->faction, resolve_faction); - READ_INT(store, &n); - w->mode = (unsigned char)n; - w->next = pl->watchers; - pl->watchers = w; - fno = read_faction_reference(store); - } -} - int readgame(const char *filename, bool backup) { int n, p, nread; @@ -1528,7 +1556,13 @@ int readgame(const char *filename, bool backup) } } else { - read_watchers(&store, pl); + /* WATCHERS - eliminated in February 2016, ca. turn 966 */ + if (gdata.version < CRYPT_VERSION) { + variant fno; + do { + fno = read_faction_reference(&store); + } while (fno.i); + } } a_read(&store, &pl->attribs, pl); if (pl->id != 1094969858) { // Regatta @@ -1815,7 +1849,9 @@ int writegame(const char *filename) WRITE_INT(&store, pl->miny); WRITE_INT(&store, pl->maxy); WRITE_INT(&store, pl->flags); - write_watchers(&store, pl); +#if RELEASE_VERSION < CRYPT_VERSION + write_faction_reference(NULL, &store); /* mark the end of pl->watchers (gone since T966) */ +#endif a_write(&store, pl->attribs, pl); WRITE_SECTION(&store); } diff --git a/src/kernel/save.h b/src/kernel/save.h index ca364fc9c..cedd5befc 100644 --- a/src/kernel/save.h +++ b/src/kernel/save.h @@ -83,6 +83,9 @@ extern "C" { struct gamedata *gamedata_open(const char *filename, const char *mode); 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 } #endif diff --git a/src/kernel/save.test.c b/src/kernel/save.test.c index e7f85ad7e..de7f28084 100644 --- a/src/kernel/save.test.c +++ b/src/kernel/save.test.c @@ -3,11 +3,25 @@ #include "save.h" #include "unit.h" +#include "group.h" +#include "ally.h" #include "faction.h" +#include "plane.h" +#include "region.h" #include "version.h" +#include +#include +#include +#include +#include +#include +#include + #include #include +#include + #include static void test_readwrite_data(CuTest * tc) @@ -42,15 +56,17 @@ static void test_readwrite_unit(CuTest * tc) u = test_create_unit(f, r); join_path(datapath(), filename, path, sizeof(path)); - data = gamedata_open(path, "w"); - CuAssertPtrNotNull(tc, data); // TODO: intermittent test + data = gamedata_open(path, "wb"); + CuAssertPtrNotNull(tc, data); + write_unit(data, u); gamedata_close(data); free_gamedata(); f = test_create_faction(0); renumber_faction(f, fno); - data = gamedata_open(path, "r"); + data = gamedata_open(path, "rb"); + CuAssertPtrNotNull(tc, data); u = read_unit(data); gamedata_close(data); @@ -61,10 +77,189 @@ static void test_readwrite_unit(CuTest * tc) test_cleanup(); } +static void test_readwrite_dead_faction_group(CuTest *tc) { + faction *f, *f2; + unit * u; + group *g; + ally *al; + int fno; + + test_cleanup(); + f = test_create_faction(0); + fno = f->no; + CuAssertPtrEquals(tc, f, factions); + CuAssertPtrEquals(tc, 0, f->next); + f2 = test_create_faction(0); + CuAssertPtrEquals(tc, f2, factions->next); + u = test_create_unit(f2, test_create_region(0, 0, 0)); + CuAssertPtrNotNull(tc, u); + g = join_group(u, "group"); + CuAssertPtrNotNull(tc, g); + al = ally_add(&g->allies, f); + CuAssertPtrNotNull(tc, al); + + CuAssertPtrEquals(tc, f, factions); + destroyfaction(&factions); + CuAssertTrue(tc, !f->_alive); + CuAssertPtrEquals(tc, f2, factions); + writegame("test.dat"); + free_gamedata(); + f = f2 = NULL; + readgame("test.dat", false); + CuAssertPtrEquals(tc, 0, findfaction(fno)); + f2 = factions; + CuAssertPtrNotNull(tc, f2); + u = f2->units; + CuAssertPtrNotNull(tc, u); + g = get_group(u); + CuAssertPtrNotNull(tc, g); + CuAssertPtrEquals(tc, 0, g->allies); + test_cleanup(); +} + +static void test_readwrite_dead_faction_regionowner(CuTest *tc) { + faction *f; + region *r; + + test_cleanup(); + config_set("rules.region_owners", "1"); + f = test_create_faction(0); + test_create_unit(f, r = test_create_region(0, 0, 0)); + region_set_owner(r, f, turn); + destroyfaction(&factions); + CuAssertTrue(tc, !f->_alive); + remove_empty_units(); + writegame("test.dat"); + free_gamedata(); + f = NULL; + readgame("test.dat", false); + f = factions; + CuAssertPtrEquals(tc, 0, f); + r = regions; + CuAssertPtrNotNull(tc, r); + CuAssertPtrEquals(tc, 0, region_get_owner(r)); + test_cleanup(); +} + +static void test_readwrite_dead_faction_changefaction(CuTest *tc) { + faction *f, *f2; + region *r; + trigger *tr; + unit * u; + + test_cleanup(); + f = test_create_faction(0); + f2 = test_create_faction(0); + u = test_create_unit(f2, r = test_create_region(0, 0, 0)); + tr = trigger_changefaction(u, f); + add_trigger(&u->attribs, "timer", trigger_timeout(10, tr)); + CuAssertPtrNotNull(tc, a_find(u->attribs, &at_eventhandler)); + destroyfaction(&factions); + CuAssertTrue(tc, !f->_alive); + remove_empty_units(); + writegame("test.dat"); + free_gamedata(); + f = NULL; + readgame("test.dat", false); + f = factions; + CuAssertPtrNotNull(tc, f); + r = regions; + CuAssertPtrNotNull(tc, r); + u = r->units; + CuAssertPtrNotNull(tc, u); + CuAssertPtrEquals(tc, 0, a_find(u->attribs, &at_eventhandler)); + test_cleanup(); +} + +static void test_readwrite_dead_faction_createunit(CuTest *tc) { + faction *f, *f2; + region *r; + trigger *tr; + unit * u; + + test_cleanup(); + f = test_create_faction(0); + f2 = test_create_faction(0); + u = test_create_unit(f2, r = test_create_region(0, 0, 0)); + tr = trigger_createunit(r, f, f->race, 1); + add_trigger(&u->attribs, "timer", trigger_timeout(10, tr)); + CuAssertPtrNotNull(tc, a_find(u->attribs, &at_eventhandler)); + destroyfaction(&factions); + CuAssertTrue(tc, !f->_alive); + remove_empty_units(); + writegame("test.dat"); + free_gamedata(); + f = NULL; + readgame("test.dat", false); + f = factions; + CuAssertPtrNotNull(tc, f); + r = regions; + CuAssertPtrNotNull(tc, r); + u = r->units; + CuAssertPtrNotNull(tc, u); + CuAssertPtrEquals(tc, 0, a_find(u->attribs, &at_eventhandler)); + 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 *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_readwrite_data); SUITE_ADD_TEST(suite, test_readwrite_unit); + SUITE_ADD_TEST(suite, test_readwrite_dead_faction_createunit); + SUITE_ADD_TEST(suite, test_readwrite_dead_faction_changefaction); + SUITE_ADD_TEST(suite, test_readwrite_dead_faction_regionowner); + 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; } diff --git a/src/kernel/version.h b/src/kernel/version.h index c18957530..3ff4e927a 100644 --- a/src/kernel/version.h +++ b/src/kernel/version.h @@ -33,9 +33,10 @@ #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 CRYPT_VERSION 351 /* passwords are encrypted */ -#define RELEASE_VERSION CRYPT_VERSION /* current datafile */ +#define BADCRYPT_VERSION 351 /* passwords are encrypted, poorly */ +#define CRYPT_VERSION 352 /* passwords are encrypted */ +#define RELEASE_VERSION ATTRIBOWNER_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 */ +#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 */ diff --git a/src/laws.c b/src/laws.c index eb079b8ac..1e1fc33ad 100755 --- a/src/laws.c +++ b/src/laws.c @@ -2172,7 +2172,7 @@ int password_cmd(unit * u, struct order *ord) cmistake(u, ord, 283, MSG_EVENT); 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", "value", pwbuf)); return 0; diff --git a/src/reports.c b/src/reports.c index b33f5f1cb..595f92ae3 100644 --- a/src/reports.c +++ b/src/reports.c @@ -1385,17 +1385,9 @@ static void prepare_reports(void) for (r = regions; r; r = r->next) { unit *u; - plane *p = rplane(r); reorder_units(r); - if (p) { - watcher *w = p->watchers; - for (; w; w = w->next) { - faction_add_seen(w->faction, r, w->mode); - } - } - /* Region owner get always the Lighthouse report */ if (bt_lighthouse && config_token("rules.region_owner_pay_building", bt_lighthouse->_name)) { for (b = rbuildings(r); b; b = b->next) { @@ -1464,8 +1456,6 @@ static region *lastregion(faction * f) /* we continue from the best region and look for travelthru etc. */ for (r = f->last->next; r; r = r->next) { - plane *p = rplane(r); - /* search the region for travelthru-attributes: */ if (fval(r, RF_TRAVELUNIT)) { travelthru_map(r, cb_set_last, f); @@ -1474,9 +1464,6 @@ static region *lastregion(faction * f) continue; if (check_leuchtturm(r, f)) f->last = r; - if (p && is_watcher(p, f)) { - f->last = r; - } } return f->last->next; #else diff --git a/src/tests.c b/src/tests.c index b8508b4cc..b52759489 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5,6 +5,7 @@ #include "prefix.h" #include +#include #include #include #include @@ -47,7 +48,7 @@ struct region *test_create_region(int x, int y, const terrain_type *terrain) { region *r = findregion(x, y); if (!r) { - r = new_region(x, y, NULL, 0); + r = new_region(x, y, findplane(x, y), 0); } if (!terrain) { terrain_type *t = get_or_create_terrain("plain"); diff --git a/src/triggers/changefaction.c b/src/triggers/changefaction.c index eb212b769..47eee5874 100644 --- a/src/triggers/changefaction.c +++ b/src/triggers/changefaction.c @@ -79,14 +79,20 @@ static void changefaction_write(const trigger * t, struct storage *store) { changefaction_data *td = (changefaction_data *)t->data.v; write_unit_reference(td->unit, store); - write_faction_reference(td->faction, store); + write_faction_reference(td->faction->_alive ? td->faction : NULL, store); } static int changefaction_read(trigger * t, struct storage *store) { + variant var; changefaction_data *td = (changefaction_data *)t->data.v; read_reference(&td->unit, store, read_unit_reference, resolve_unit); - read_reference(&td->faction, store, read_faction_reference, resolve_faction); + var = read_faction_reference(store); + if (var.i == 0) { + return AT_READ_FAIL; + } + ur_add(var, &td->faction, resolve_faction); + // read_reference(&td->faction, store, read_faction_reference, resolve_faction); return AT_READ_OK; } diff --git a/src/triggers/createunit.c b/src/triggers/createunit.c index 642e598f0..ef79d4300 100644 --- a/src/triggers/createunit.c +++ b/src/triggers/createunit.c @@ -82,7 +82,7 @@ static int createunit_handle(trigger * t, void *data) static void createunit_write(const trigger * t, struct storage *store) { createunit_data *td = (createunit_data *)t->data.v; - write_faction_reference(td->f, store); + write_faction_reference(td->f->_alive ? td->f : NULL, store); write_region_reference(td->r, store); write_race_reference(td->race, store); WRITE_INT(store, td->number); @@ -91,21 +91,27 @@ static void createunit_write(const trigger * t, struct storage *store) static int createunit_read(trigger * t, struct storage *store) { createunit_data *td = (createunit_data *)t->data.v; + variant var; + int result = AT_READ_OK; - int uc = - read_reference(&td->f, store, read_faction_reference, resolve_faction); - int rc = - read_reference(&td->r, store, read_region_reference, + var = read_faction_reference(store); + if (var.i > 0) { + td->f = findfaction(var.i); + if (!td->f) { + ur_add(var, &td->f, resolve_faction); + } + } + else { + result = AT_READ_FAIL; + } + read_reference(&td->r, store, read_region_reference, RESOLVE_REGION(global.data_version)); td->race = (const struct race *)read_race_reference(store).v; - - if (uc == 0 && rc == 0) { - if (!td->f || !td->r) - return AT_READ_FAIL; + if (!td->race) { + result = AT_READ_FAIL; } READ_INT(store, &td->number); - - return AT_READ_OK; + return result; } trigger_type tt_createunit = { diff --git a/src/util/gamedata.test.c b/src/util/gamedata.test.c new file mode 100644 index 000000000..b28066b97 --- /dev/null +++ b/src/util/gamedata.test.c @@ -0,0 +1,25 @@ +#include +#include "gamedata.h" + +#include +#include +#include + +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; +} diff --git a/src/util/password.c b/src/util/password.c index 271cedf6d..c7850eec8 100644 --- a/src/util/password.c +++ b/src/util/password.c @@ -2,7 +2,9 @@ #include "password.h" #include +#include #include +#include #include #include @@ -25,73 +27,104 @@ } while (0) -char *password_gensalt(void) { - static char salt[SALTLEN + 1]; +char *password_gensalt(char *salt, size_t salt_len) { + size_t buflen = salt_len-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); + 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; } -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) { - return md5_crypt_r(passwd, salt, result, len); - } - else if (algo == PASSWORD_APACHE_MD5) { - apr_md5_encode(passwd, salt, result, len); +static bool password_is_implemented(int algo) { + return algo == PASSWORD_PLAINTEXT || algo == PASSWORD_BCRYPT || algo == PASSWORD_NOCRYPT || algo == PASSWORD_MD5 || algo == PASSWORD_APACHE_MD5; +} + +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); + } + if (_crypt_gensalt_blowfish_rn("$2y$", 5, input, strlen(input), setting, sizeof(setting)) == NULL) { + return NULL; + } + if (_crypt_blowfish_rn(passwd, setting, result, len) == NULL) { + return NULL; + } return result; } - else { - return NULL; + else if (algo == PASSWORD_PLAINTEXT) { + _snprintf(result, len, "%s", passwd); + return result; } - return 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; } -const char * password_hash(const char * passwd, const char * salt, int algo) { +const char * password_encode(const char * passwd, 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 || algo==PASSWORD_APACHE_MD5; + return password_hash_i(passwd, 0, algo, result, sizeof(result)); } int password_verify(const char * pwhash, const char * passwd) { - char salt[MAXSALTLEN+1]; char hash[64]; - size_t len; - int algo; + int algo = PASSWORD_PLAINTEXT; char *pos; - const char *dol, *result; + const char *result; assert(passwd); assert(pwhash); - assert(pwhash[0] == '$'); - 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 (pwhash[0] == '$') { + algo = pwhash[1]; + } if (!password_is_implemented(algo)) { 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) { return VERIFY_OK; } diff --git a/src/util/password.h b/src/util/password.h index 0bdd9d4ff..fe84716d7 100644 --- a/src/util/password.h +++ b/src/util/password.h @@ -1,15 +1,16 @@ #pragma once -#define PASSWORD_PLAIN '0' +#define PASSWORD_PLAINTEXT 0 +#define PASSWORD_NOCRYPT '0' #define PASSWORD_MD5 '1' #define PASSWORD_BCRYPT '2' // not implemented #define PASSWORD_APACHE_MD5 'a' #define PASSWORD_SHA256 '5' // 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_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); +const char * password_encode(const char *passwd, int algo); diff --git a/src/util/password.test.c b/src/util/password.test.c index b4f4005da..3df2d2ed8 100644 --- a/src/util/password.test.c +++ b/src/util/password.test.c @@ -1,25 +1,43 @@ #include -#include #include "password.h" +#include +#include 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); - CuAssertStrEquals(tc, "$apr1$FqQLkl8g$.icQqaDJpim4BVy.Ho5660", hash); - CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "Hodor")); + CuAssertIntEquals(tc, 0, strncmp(hash, expect, 6)); - 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); - CuAssertStrEquals(tc, "$1$ZouUn04i$yNnT1Oy8azJ5V.UM9ppP5/", hash); - CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "jollygood")); + CuAssertIntEquals(tc, 0, strncmp(hash, expect, 3)); - hash = password_hash("password", "hodor", PASSWORD_PLAIN); + expect = "password"; + hash = password_encode("password", PASSWORD_PLAINTEXT); 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")); + CuAssertStrEquals(tc, hash, expect); + CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "password")); + 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")); } diff --git a/tests/write-reports.sh b/tests/write-reports.sh index c87059854..2183b17dd 100755 --- a/tests/write-reports.sh +++ b/tests/write-reports.sh @@ -25,7 +25,7 @@ TESTS=../Debug/eressea/test_eressea SERVER=../Debug/eressea/eressea if [ -n "$VALGRIND" ]; then 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 echo "running $TESTS" $VALGRIND $TESTS