diff --git a/source/src/client.cpp b/source/src/client.cpp index d75c0200..a592f905 100644 --- a/source/src/client.cpp +++ b/source/src/client.cpp @@ -49,11 +49,13 @@ void throttle() } string clientpassword = ""; +int claimadmin = 0; void abortconnect() { if(!connpeer) return; clientpassword[0] = '\0'; + claimadmin = 0; if(connpeer->state!=ENET_PEER_STATE_DISCONNECTED) enet_peer_reset(connpeer); connpeer = NULL; #if 0 @@ -88,6 +90,7 @@ void connects(char *servername, char *serverport, char *password) { conoutf("\f3could not resolve server %s", servername); clientpassword[0] = '\0'; + claimadmin = 0; return; } } @@ -111,14 +114,15 @@ void connects(char *servername, char *serverport, char *password) { conoutf("\f3could not connect to server"); clientpassword[0] = '\0'; + claimadmin = 0; } } void connectadmin(char *servername, char *serverport, char *password) { if(!password) return; + claimadmin = 1; connects(servername, serverport, password); - if(connmillis >= totalmillis) addmsg(SV_SETADMIN, "ris", 1, password); // in case the server is not private locked or pwd protected } void lanconnect() @@ -374,8 +378,10 @@ void sendintro() ucharbuf p(packet->data, packet->dataLength); putint(p, SV_CONNECT); sendstring(player1->name, p); - sendstring(clientpassword, p); + sendstring(genpwdhash(player1->name, clientpassword, 0), p); + putint(p, claimadmin); clientpassword[0] = '\0'; + claimadmin = 0; putint(p, player1->nextprimweap->type); enet_packet_resize(packet, p.length()); sendpackettoserv(1, packet); @@ -532,39 +538,3 @@ COMMAND(resetsecuremaps, ARG_NONE); COMMAND(securemap, ARG_1STR); COMMAND(getdemo, ARG_1INT); COMMAND(listdemos, ARG_NONE); - -static cvector playerskinlist; - -const char *getclientskin(const char *name, const char *suf) -{ - static string tmp; - int suflen = strlen(suf), namelen = strlen(name); - const char *s, *r = NULL; - loopv(playerskinlist) - { - s = playerskinlist[i]; - int sl = strlen(s) - suflen; - if(sl > 0 && !strcmp(s + sl, suf)) - { - if(!strncmp(name, s, namelen)) return s; // exact match - if(s[sl - 1] == '_') - { - s_strcpy(tmp, s); - tmp[sl - 1] = '\0'; - if(strstr(name, tmp)) r = s; // partial match - } - } - } - return r; -} - -void updateclientname(playerent *d) -{ - static bool gotlist = false; - if(!gotlist) listfiles("packages/models/playermodels/custom", "jpg", playerskinlist); - gotlist = true; - if(!d || !playerskinlist.length()) return; - d->skin_noteam = getclientskin(d->name, "_ffa"); - d->skin_cla = getclientskin(d->name, "_cla"); - d->skin_rvsf = getclientskin(d->name, "_rvsf"); -} diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index cc2b579b..a1c29546 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -982,11 +982,13 @@ void whois(int cn) COMMAND(whois, ARG_1INT); +int setadminsalt = 0; + void setadmin(char *claim, char *password) { - if(!claim) return; - if(strlen(password) > 15) { conoutf("the admin password has a maximum length of 15 characters"); return; } - else addmsg(SV_SETADMIN, "ris", atoi(claim) != 0 ? 1 : 0, password); + if(!claim || !password) return; + else addmsg(SV_SETADMIN, "ris", atoi(claim), genpwdhash(player1->name, password, setadminsalt)); + conoutf("%d %s", setadminsalt, genpwdhash(player1->name, password, setadminsalt)); } COMMAND(setadmin, ARG_2STR); diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index 518ebfcd..e2167ddb 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -164,6 +164,7 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) case SV_INITS2C: // welcome messsage from the server { int mycn = getint(p), prot = getint(p); + setadminsalt = getint(p); if(prot!=PROTOCOL_VERSION) { conoutf("you are using a different game protocol (you: %d, server: %d)", PROTOCOL_VERSION, prot); diff --git a/source/src/hash.h b/source/src/hash.h new file mode 100644 index 00000000..a45c68bd --- /dev/null +++ b/source/src/hash.h @@ -0,0 +1,149 @@ +/* Based off the reference implementation of Tiger, a cryptographically + * secure 192 bit hash function by Ross Anderson and Eli Biham. More info at: + * http://www.cs.technion.ac.il/~biham/Reports/Tiger/ + */ + +#define TIGER_PASSES 3 + +struct tiger +{ + typedef unsigned long long int chunk; + + union hashval + { + uchar bytes[3*8]; + chunk chunks[3]; + }; + + static chunk sboxes[4*256]; + + static void gensboxes() + { + const char *str = "Tiger - A Fast New Hash Function, by Ross Anderson and Eli Biham"; + chunk state[3] = { 0x0123456789ABCDEFULL, 0xFEDCBA9876543210ULL, 0xF096A5B4C3B2E187ULL }; + uchar temp[64]; + + #if SDL_BYTEORDER == SDL_BIG_ENDIAN + loopj(64) temp[j^7] = str[j]; + #else + loopj(64) temp[j] = str[j]; + #endif + loopi(1024) loop(col, 8) ((uchar *)&sboxes[i])[col] = i&0xFF; + + int abc = 2; + loop(pass, 5) loopi(256) for(int sb = 0; sb < 1024; sb += 256) + { + abc++; + if(abc >= 3) { abc = 0; compress((chunk *)temp, state); } + loop(col, 8) + { + uchar val = ((uchar *)&sboxes[sb+i])[col]; + ((uchar *)&sboxes[sb+i])[col] = ((uchar *)&sboxes[sb + ((uchar *)&state[abc])[col]])[col]; + ((uchar *)&sboxes[sb + ((uchar *)&state[abc])[col]])[col] = val; + } + } + } + + static void compress(const chunk *str, chunk state[3]) + { + chunk a, b, c; + chunk aa, bb, cc; + chunk x0, x1, x2, x3, x4, x5, x6, x7; + + a = state[0]; + b = state[1]; + c = state[2]; + + x0=str[0]; x1=str[1]; x2=str[2]; x3=str[3]; + x4=str[4]; x5=str[5]; x6=str[6]; x7=str[7]; + + aa = a; + bb = b; + cc = c; + + loop(pass_no, TIGER_PASSES) + { + if(pass_no) + { + x0 -= x7 ^ 0xA5A5A5A5A5A5A5A5ULL; x1 ^= x0; x2 += x1; x3 -= x2 ^ ((~x1)<<19); + x4 ^= x3; x5 += x4; x6 -= x5 ^ ((~x4)>>23); x7 ^= x6; + x0 += x7; x1 -= x0 ^ ((~x7)<<19); x2 ^= x1; x3 += x2; + x4 -= x3 ^ ((~x2)>>23); x5 ^= x4; x6 += x5; x7 -= x6 ^ 0x0123456789ABCDEFULL; + } + +#define sb1 (sboxes) +#define sb2 (sboxes+256) +#define sb3 (sboxes+256*2) +#define sb4 (sboxes+256*3) + +#define round(a, b, c, x) \ + c ^= x; \ + a -= sb1[((c)>>(0*8))&0xFF] ^ sb2[((c)>>(2*8))&0xFF] ^ \ + sb3[((c)>>(4*8))&0xFF] ^ sb4[((c)>>(6*8))&0xFF] ; \ + b += sb4[((c)>>(1*8))&0xFF] ^ sb3[((c)>>(3*8))&0xFF] ^ \ + sb2[((c)>>(5*8))&0xFF] ^ sb1[((c)>>(7*8))&0xFF] ; \ + b *= mul; + + uint mul = !pass_no ? 5 : (pass_no==1 ? 7 : 9); + round(a, b, c, x0) round(b, c, a, x1) round(c, a, b, x2) round(a, b, c, x3) + round(b, c, a, x4) round(c, a, b, x5) round(a, b, c, x6) round(b, c, a, x7) + + chunk tmp = a; a = c; c = b; b = tmp; + } + + a ^= aa; + b -= bb; + c += cc; + + state[0] = a; + state[1] = b; + state[2] = c; + } + + static void hash(const uchar *str, int length, hashval &val) + { + static bool init = false; + if(!init) { gensboxes(); init = true; } + + uchar temp[64]; + + val.chunks[0] = 0x0123456789ABCDEFULL; + val.chunks[1] = 0xFEDCBA9876543210ULL; + val.chunks[2] = 0xF096A5B4C3B2E187ULL; + + int i = length; + for(; i >= 64; i -= 64, str += 64) + { +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + loopj(64) temp[j^7] = str[j]; + compress((chunk *)temp, val.chunks); +#else + compress((chunk *)str, val.chunks); +#endif + } + + int j; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + for(j = 0; j < i; j++) temp[j^7] = str[j]; + temp[j^7] = 0x01; + while(++j&7) temp[j^7] = 0; +#else + for(j = 0; j < i; j++) temp[j] = str[j]; + temp[j] = 0x01; + while(++j&7) temp[j] = 0; +#endif + + if(j > 56) + { + while(j < 64) temp[j++] = 0; + compress((chunk *)temp, val.chunks); + j = 0; + } + while(j < 56) temp[j++] = 0; + *(chunk *)(temp+56) = (chunk)length<<3; + compress((chunk *)temp, val.chunks); + } +}; + +tiger::chunk tiger::sboxes[4*256]; + diff --git a/source/src/protocol.cpp b/source/src/protocol.cpp index 8120be85..ffc62470 100644 --- a/source/src/protocol.cpp +++ b/source/src/protocol.cpp @@ -2,6 +2,7 @@ #include "pch.h" #include "cube.h" +#include "hash.h" #ifdef _DEBUG bool protocoldbg = false; @@ -162,3 +163,14 @@ char msgsizelookup(int msg) return -1; } +const char *genpwdhash(const char *name, const char *pwd, int salt) +{ + static string temp; + s_sprintf(temp)("%s %d %s %s %d", pwd, salt, name, pwd, PROTOCOL_VERSION); + tiger::hashval hash; + tiger::hash((uchar *)temp, strlen(temp), hash); + s_sprintf(temp)("%llx %llx %llx", hash.chunks[0], hash.chunks[1], hash.chunks[2]); + return temp; +} + + diff --git a/source/src/protos.h b/source/src/protos.h index d1a277c0..cabfcb6e 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -306,10 +306,10 @@ extern bool securemapcheck(char *map, bool msg = true); extern void sendintro(); extern void getdemo(int i); extern void listdemos(); -extern void updateclientname(playerent *d); // clientgame extern flaginfo flaginfos[2]; +extern int setadminsalt; extern bool autoteambalance; extern void updateworld(int curtime, int lastmillis); extern void resetmap(); @@ -576,6 +576,7 @@ extern void preload_mapmodels(); extern void renderclients(); extern void renderclient(playerent *d); extern void renderclient(playerent *d, const char *mdlname, const char *vwepname, int tex = 0); +extern void updateclientname(playerent *d); // weapon extern void shoot(playerent *d, vec &to); @@ -640,6 +641,7 @@ extern void restoreserverstate(vector &ents); extern uchar *retrieveservers(uchar *buf, int buflen); extern void serverms(int mode, int numplayers, int minremain, char *smapname, int millis, const ENetAddress &localaddr); extern char msgsizelookup(int msg); +extern const char *genpwdhash(const char *name, const char *pwd, int salt); extern void servermsdesc(const char *sdesc); extern void servermsinit(const char *master, const char *ip, int serverport, const char *sdesc, bool listen); extern bool serverpickup(int i, int sender); diff --git a/source/src/rendermodel.cpp b/source/src/rendermodel.cpp index dfb3e3b0..fc531c52 100644 --- a/source/src/rendermodel.cpp +++ b/source/src/rendermodel.cpp @@ -636,12 +636,49 @@ void renderclient(playerent *d, const char *mdlname, const char *vwepname, int t } VARP(teamdisplaymode, 0, 1, 2); + +#define SKINBASE "packages/models/playermodels" VARP(hidecustomskins, 0, 0, 1); +static cvector playerskinlist; + +const char *getclientskin(const char *name, const char *suf) +{ + static string tmp; + int suflen = strlen(suf), namelen = strlen(name); + const char *s, *r = NULL; + loopv(playerskinlist) + { + s = playerskinlist[i]; + int sl = strlen(s) - suflen; + if(sl > 0 && !strcmp(s + sl, suf)) + { + if(!strncmp(name, s, namelen)) return s; // exact match + if(s[sl - 1] == '_') + { + s_strcpy(tmp, s); + tmp[sl - 1] = '\0'; + if(strstr(name, tmp)) r = s; // partial match + } + } + } + return r; +} + +void updateclientname(playerent *d) +{ + static bool gotlist = false; + if(!gotlist) listfiles(SKINBASE "/custom", "jpg", playerskinlist); + gotlist = true; + if(!d || !playerskinlist.length()) return; + d->skin_noteam = getclientskin(d->name, "_ffa"); + d->skin_cla = getclientskin(d->name, "_cla"); + d->skin_rvsf = getclientskin(d->name, "_rvsf"); +} void renderclient(playerent *d) { if(!d) return; - const char *cs = NULL, *skinbase = "packages/models/playermodels"; + const char *cs = NULL, *skinbase = SKINBASE; int team = team_int(d->team); int skinid = 1 + max(0, min(d->skin, (team==TEAM_CLA ? 3 : 5))); string skin; diff --git a/source/src/server.cpp b/source/src/server.cpp index cefa1b10..b7cca3de 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -221,6 +221,7 @@ struct client // server side version of "dynent" type int at3_score, at3_lastforce, lastforce; bool at3_dontmove; int spawnindex; + int salt; gameevent &addevent() { @@ -1609,12 +1610,12 @@ void readpwdfile(const char *name) logger->writeline(log::info,"read %d admin passwords from %s", adminpwds.length() - (adminpasswd && adminpasswd[0]), name ? name : ""); } -bool checkadmin(const char *pwd, pwddetail *detail = NULL) +bool checkadmin(const char *name, const char *pwd, int salt, pwddetail *detail = NULL) { bool found = false; loopv(adminpwds) { - if(!strcmp(adminpwds[i].pwd, pwd)) + if(!strcmp(genpwdhash(name, adminpwds[i].pwd, salt), pwd)) { if(detail) *detail = adminpwds[i]; found = true; @@ -1902,7 +1903,7 @@ void changeclientrole(int client, int role, char *pwd = NULL, bool force=false) pwddetail pd; if(!isdedicated || !valid_client(client)) return; pd.line = -1; - if(force || role == CR_DEFAULT || (role == CR_ADMIN && pwd && pwd[0] && checkadmin(pwd, &pd) && !pd.denyadmin)) + if(force || role == CR_DEFAULT || (role == CR_ADMIN && pwd && pwd[0] && checkadmin(clients[client]->name, pwd, clients[client]->salt, &pd) && !pd.denyadmin)) { if(role == clients[client]->role) return; if(role > CR_DEFAULT) @@ -2220,6 +2221,13 @@ void welcomepacket(ucharbuf &p, int n, ENetPacket *packet) putint(p, SV_INITS2C); putint(p, n); putint(p, PROTOCOL_VERSION); + client *c = valid_client(n) ? clients[n] : NULL; + if(c) + { + c->salt = rand() * (servmillis % 999); + putint(p, c->salt); + } + else putint(p, 0); if(!smapname[0] && configsets.length()) nextcfgset(false); int numcl = numclients(); putint(p, smapname[0] && !m_demo ? numcl : -1); @@ -2252,7 +2260,6 @@ void welcomepacket(ucharbuf &p, int n, ENetPacket *packet) loopi(2) putflaginfo(p, i); } } - client *c = valid_client(n) ? clients[n] : NULL; if(c && c->type == ST_TCPIP && serveroperator() != -1) sendserveropinfo(n); if(numcl>1) { @@ -2383,14 +2390,15 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1 s_strncpy(cl->name, text, MAXNAMELEN+1); getstring(text, p); + int cadm = getint(p); cl->state.nextprimary = getint(p); bool banned = isbanned(sender); bool srvfull = numnonlocalclients() > maxclients; bool srvprivate = mastermode == MM_PRIVATE; - if(checkadmin(text, &pd) && (!pd.denyadmin || (banned && !srvfull && !srvprivate))) // pass admins always through + if(checkadmin(cl->name, text, 0, &pd) && (!pd.denyadmin || (banned && !srvfull && !srvprivate))) // pass admins always through { cl->isauthed = true; - if(!pd.denyadmin) clientrole = CR_ADMIN; + if(!pd.denyadmin && cadm > 0) clientrole = CR_ADMIN; if(banned) { loopv(bans) if(bans[i].address.host == cl->peer->address.host) { bans.remove(i); break; } // remove admin bans @@ -2762,7 +2770,7 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1 { // save score savedscore *sc = findscore(*cl, true); - if(sc) sc->save(cl->state); + if(sc) sc->save(cl->state); // resend state properly sendpacket(cl->clientnum, 2, mappacket); cl->mapchange();