Merge pull request #352 from RonaldReagan/authimprovements_263

Improve current auth system 263
This commit is contained in:
drian0
2021-05-22 06:14:14 +02:00
committed by GitHub
17 changed files with 404 additions and 138 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
// try to authenticate game account during startup in silent mode so that users with saved passwords are authenticated asap
sleep 1000 [ checkaccount 1 ]
sleep 100 [ checkaccount 1 ]
sleep 1000 [
echo "\fs\f3Beware: This is an ALPHA version of AssaultCube v1.3"
+2 -2
View File
@@ -7388,9 +7388,9 @@ docremark ["authsetup genpriv" - generates private key from prepriv key.];
docremark ["authsetup genpub" - generates public key from private key.];
docremark ["authsetup newppass preprivpass [preprivfilename]" - encrypts prepriv key with new password and saves the result to file. The default filename is config/authpreprivate.cfg and an existing file gets overwritten - but the former file content is kept and also written, each line commented out.];
docremark ["authsetup newppass preprivpass [preprivfilename]" - encrypts prepriv key with new password and saves the result to file. The default filename is private/authpreprivate.cfg and an existing file gets overwritten - but the former file content is kept and also written, each line commented out.];
docremark ["authsetup newpass privpass [privatefilename]" - encrypts private key with new password and saves the encrypted private key and the public key to file. The default filename is config/authprivate.cfg and an existing file gets overwritten - but the former file content is kept and also written, each line commented out.];
docremark ["authsetup newpass privpass [privatefilename]" - encrypts private key with new password and saves the encrypted private key and the public key to file. The default filename is private/authprivate.cfg and an existing file gets overwritten - but the former file content is kept and also written, each line commented out.];
docremark ["authsetup unarmed" - sets private and public keys to a fixed value. For testing only. Can be used to "log out".];
+1
View File
@@ -143,6 +143,7 @@ exec config/menus_bot.cfg
exec config/menus_edit.cfg
exec config/menus_licenses.cfg
exec config/menus_settings.cfg
exec config/menus_auth.cfg
exec config/menus_multiplayer.cfg
exec config/menus_voicecom.cfg
exec config/favourites.cfg
+147
View File
@@ -0,0 +1,147 @@
newmenu [auth setup]
menuinit [ tempalias ___ppass "" ; tempalias ___pass ""; tempalias ___rememberpass 0; ]
menuitem "To play online you need to create a game account which will be used" -1
menuitem "identify you to game servers. This identity is stored as a public/private" -1
menuitem "keypair and only the public key is ever transmitted to the server. You are" -1
menuitem "able to change the password at a future point in time." -1
menuitem "" -1
menuitem "There exists a mechanism to recover your identity if lost that uses a" -1
menuitem "seperate password." -1
menuitem "" -1
menuitem "To learn more about this functionality you can go to this URL in your browser" -1
menuitem "\f1https://assault.cubers.net/docs/auth.html" -1
menuitem "" -1
menuitem "\f0Set passwords \f4(recommended)"
menuitemtextinput "Password for your game account\t" "" [___pass = $arg1] [] 64
menuitemtextinput "Password for account restoration\t" "" [___ppass = $arg1] [] 64
menuitem "" -1
menuitemradio "Remember game account password: " 0 1 [ ] [ "No \f4(recommended)" "Yes"] [ ___rememberpass = (= $arg1 1)]
menuitem "\f2\t\t\t [ OK ]" [
authsetup genpre
authsetup genpriv
authsetup genpub
if (= $___rememberpass 1) [ alias savedpass $___pass ] [ if (checkalias savedpass) [ delalias savedpass ] ]
closecurmenu
showmenu "auth setup progress"
]
newmenu "auth setup progress"
menuinit [ sleep 100 [
if (strcmp $___ppass "") [ authsetup savepre ] [ authsetup newppass $___ppass; authsetup savepre ]
if (strcmp $___pass "") [ authsetup savepriv ] [ authsetup newpass $___pass ]
___ppass = ""
___pass = ""
if (authsetup) [ echo "\f0created game account" ]
closecurmenu
checkaccount
] ]
menuitem "Please wait while we create your account..." -1
newmenu [auth password change]
menuinit [ tempalias ___pass ""; tempalias ___rememberpass 0; ]
menuitem "Please choose a new password for your private key" -1
menuitem "" -1
menuitemtextinput "Password for your game account\t" "" [___pass = $arg1] [] 64
menuitemradio "Remember game account password: " 0 1 [ ] [ "No \f4(recommended)" "Yes"] [ ___rememberpass = (= $arg1 1)]
menuitem "\f2[ Set Password ]" [
if (= $___rememberpass 1) [ alias savedpass $___pass ] [ if (checkalias savedpass) [ delalias savedpass ] ]
closecurmenu
showmenu "auth password change progress"
]
menuitem "\f7[ Cancel ]" closecurmenu
newmenu "auth password change progress"
menuinit [ sleep 100 [
if (strcmp $___pass "") [ ] [ authsetup newpass $___pass ]
___pass = ""
if (authsetup) [ echo "\f0updated game account" ]
closecurmenu
checkaccount
] ]
menuitem "Please wait while we update your account..." -1
newmenu [auth]
menuinit [
resetcurmenu
tempalias ___pass ""; tempalias ___rememberpass 0;
if (authsetup) [
menuitem "\f0Pub key:" -1
menuitem (format "\fY%1" (mypubkey)) -1
menuitem "\f2[ Copy to clipboard ]" [setclipboardtext (mypubkey)]
menuitem "" -1
menuitem "\f0Change Password" -1
menuitemtextinput "New password" "" [___pass = $arg1] [] 64
menuitemradio "Remember game account password: " 0 1 [ ] [ "No \f4(recommended)" "Yes"] [ ___rememberpass = (= $arg1 1)]
menuitem "\f2[ Update Password ]" [
if (= $___rememberpass 1) [ alias savedpass $___pass ] [ if (checkalias savedpass) [ delalias savedpass ] ]
closecurmenu
showmenu "auth password change progress"
]
menuitem "" -1
menuitem "\f3[ Reset auth ]" [ showmenu [auth setup]]
] [
if (authsetup needpass) [
menuitem "\f3Password required to authenticate \f2[ Enter password ]" [ showmenu [enter password]]
menuitem "" -1
menuitem "\f3[ Reset Auth ]" [ showmenu [auth setup] ]
] [
menuitem "\f3Auth not setup" -1
menuitem "\f2[ Setup Auth ]" [ showmenu [auth setup] ]
]
]
]
newmenu "game account"
menuitem "To play online you need to create a game account first." -1
menuitem "" -1
menuitem "\t\t\f0[Create game account]" [ closecurmenu; showmenu "auth setup"; ]
menuitem "\t\t[Not now...]" closecurmenu
newmenu "enter password"
menuitemtextinput "please enter the password for your game account\t" "" [ __loginpass = $arg1; closecurmenu; showmenu authentication ] [] 64 1
newmenu "authentication"
menuinit [ sleep 100 [ echo "checking..." ; authsetup passd $__loginpass authfinish; __loginpass = "" ] ]
menuitem "Please wait while we authenticate your game account..." -1
const authfinish [
closecurmenu
if (authsetup) [
echo "\f0authentication successful"
] [
echo "\f3authentication failed, please enter the correct password for your game account"
]
]
// try to authenticate the game account or offer to create a new game account if none exists
// set $arg1 to 1 to enable silent mode to prevent any user interactions
const checkaccount [
if (authsetup) [] [
if (authsetup needpass) [] [
// load the password protected private key
exec private/authprivate.cfg
]
if (authsetup needpass) [
if (checkalias savedpass) [
// try the saved password if there is one
authsetup passd $savedpass
] [
// saved password does not exist so ask for password
if (= $arg1 0) [ showmenu "enter password" ]
]
] [
// game account does not seem to be set up, ask user to create new game account
if (= $arg1 0) [ showmenu "game account" ]
]
]
]
+2 -81
View File
@@ -172,7 +172,7 @@ menuitem [Join a LAN server] [ ifauthsetup joinlanservermenu ]
menuitem [Custom connect] [ ifauthsetup customconnectmenu ]
menuitem [Connect as Admin] [ ifauthsetup adminconnectmenu ]
menuitem [] -1
menuitem [Reset game account] [ showmenu [create game account] ]
menuitem [Auth settings] [ showmenu [auth] ]
menuitem [Force serverlist update] [ updatefrommaster 1; ifauthsetup joinservermenu ]
//// Main > Multiplayer > Custom connect ////
@@ -330,83 +330,4 @@ menuinit [
menuitem "\f2 [ Download the servers map version and join the game ]" [ getmap ]
menuitem "\f3 [ Keep the local map version and disconnect from the server ]" [ showmenu ""; disconnect ]
menuinitselection 4
]
// game account management menus
newmenu "game account"
menuitem "To play online you need to create a game account first." -1
menuitem "" -1
menuitem "\t\t\f0[Create game account]" [ closecurmenu; showmenu "create game account"; ]
menuitem "\t\t[Not now...]" closecurmenu
newmenu "create game account"
menuinit [ tempalias ___ppass "" ; tempalias ___pass ""; tempalias ___rememberpass 0; ]
menuitemtextinput "Please enter a password for account restoration\t" "" [___ppass = $arg1] [] 64
menuitemtextinput "Please enter a password for your game account\t" "" [___pass = $arg1] [] 64
menuitem "" -1
menuitemradio "Remember game account password: " 0 1 [ ] [ "No (recommended)" "Yes"] [ ___rememberpass = (= $arg1 1)]
menuitem "" -1
menuitem "\t\t\f0[Ok]" [
if (strcmp $___ppass "") [ menuselection (curmenu) 0; -1 ] [
if (strcmp $___pass "") [ menuselection (curmenu) 1; -1 ] [
authsetup genpre
authsetup genpriv
authsetup genpub
if (= $___rememberpass 1) [ alias savedpass $___pass ] [ if (checkalias savedpass) [ delalias savedpass ] ]
closecurmenu
showmenu "progress"
]
]
]
newmenu "progress"
menuinit [ sleep 100 [
authsetup newppass $___ppass
authsetup newpass $___pass
___ppass = ""
___pass = ""
if (authsetup) [ echo "\f0created game account" ]
closecurmenu
checkaccount
] ]
menuitem "Please wait while we create your account..." -1
newmenu "enter password"
menuitemtextinput "please enter the password for your game account\t" "" [ __loginpass = $arg1; closecurmenu; showmenu authentication ] [] 64 1
newmenu "authentication"
menuinit [ sleep 100 [ echo "checking..." ; authsetup passd $__loginpass authfinish; __loginpass = "" ] ]
menuitem "Please wait while we authenticate your game account..." -1
const authfinish [
closecurmenu
if (authsetup) [
echo "\f0authentication successful"
] [
echo "\f3authentication failed, please enter the correct password for your game account"
]
]
// try to authenticate the game account or offer to create a new game account if none exists
// set $arg1 to 1 to enable silent mode to prevent any user interactions
const checkaccount [
if (authsetup) [] [
if (authsetup needpass) [] [
// load the password protected private key
exec config/authprivate.cfg
]
if (authsetup needpass) [
if (checkalias savedpass) [
// try the saved password if there is one
authsetup passd $savedpass
] [
// saved password does not exist so ask for password
if (= $arg1 0) [ showmenu "enter password" ]
]
] [
// game account does not seem to be set up, ask user to create new game account
if (= $arg1 0) [ showmenu "game account" ]
]
]
]
]
+1
View File
@@ -720,6 +720,7 @@ menuinit [
fullscreen (= $__windowtype 0)
fullscreendesktop (= $__windowtype 0)
resetgl
showmenu [auth setup]
]
if (checkalias __firstrun) [] [ __firstrun = 1; menuselection (curmenu) 9 ] // preselect nickname field
]
+11 -5
View File
@@ -1728,7 +1728,11 @@ name="AssaultCube Documentation" version="Current" xsi:schemaLocation="xml/cuber
<section name="General" sortindex="01">
<description>This section describes general identifiers.</description>
<identifiers sort="true">
<command name="setclipboardtext">
<arguments>
<argument token="T" description="Text to put in the clipboard."/>
</arguments>
</command>
<variable name="autoscreenshot">
<description>Toggle for taking an automatic screenshot during intermission.</description>
<value token="B" description="0=Off, 1=On" minValue="0" maxValue="1" defaultValue="1"/>
@@ -10212,7 +10216,7 @@ name="AssaultCube Documentation" version="Current" xsi:schemaLocation="xml/cuber
and to lock and unlock the private key with a password.
</remark>
<remark>
Private and public keys are used to authenticate the player to servers, and are loaded from config/authprivate.cfg.
Private and public keys are used to authenticate the player to servers, and are loaded from private/authprivate.cfg.
Private keys can be stored in an "encrypted with password" version - in which case the player has to enter
the password after game start. To prevent brute-force cracking of the password, the key-derivation function uses
a constant time to encode (usually several seconds). Because of that, the decoding at game start can be done
@@ -10241,17 +10245,19 @@ name="AssaultCube Documentation" version="Current" xsi:schemaLocation="xml/cuber
<remark>"authsetup genpre [prelen]" - generates new prepriv key.</remark>
<remark>"authsetup genpriv" - generates private key from prepriv key.</remark>
<remark>"authsetup genpub" - generates public key from private key.</remark>
<remark>"authsetup savepre [preprivfilename]" - saves the prepriv key. The default filename is private/authpreprivate.cfg</remark>
<remark>"authsetup savepre [privatefilename]" - saves the private key. The default filename is private/authprivate.cfg</remark>
<remark>
"authsetup newppass preprivpass [preprivfilename]" - encrypts prepriv key with new password and saves the result to file.
The default filename is config/authpreprivate.cfg and an existing file gets overwritten - but the former file
The default filename is private/authpreprivate.cfg and an existing file gets overwritten - but the former file
content is kept and also written, each line commented out.
</remark>
<remark>
"authsetup newpass privpass [privatefilename]" - encrypts private key with new password and saves the encrypted
private key and the public key to file. The default filename is config/authprivate.cfg and an existing file
private key and the public key to file. The default filename is private/authprivate.cfg and an existing file
gets overwritten - but the former file content is kept and also written, each line commented out.
</remark>
<remark>"authsetup unarmed" - sets private and public keys to a fixed value. For testing only. Can be used to "log out".</remark>
<remark>"authsetup unarmed" - sets private and public keys to a fixed value. For testing only. Can be used to "log out". Only available in development versions of the game</remark>
<remark>----------</remark>
<remark>
"authmemusage", "authrounds", "authmaxtime" are only used, when new passwords are created (authsetup new[p]pass).
+2 -2
View File
@@ -115,7 +115,6 @@ COMMANDN(connect, connectserv, "sis");
void connectadmin(char *servername, int *serverport, char *password)
{
modprotocol = false;
if(!password[0]) return;
connectserv_(servername, *serverport, password, CR_ADMIN);
}
COMMAND(connectadmin, "sis");
@@ -137,7 +136,6 @@ COMMANDN(modconnect, modconnectserv, "sis");
void modconnectadmin(char *servername, int *serverport, char *password)
{
modprotocol = true;
if(!password[0]) return;
connectserv_(servername, *serverport, password, CR_ADMIN);
}
COMMAND(modconnectadmin, "sis");
@@ -755,3 +753,5 @@ COMMANDF(timestring, "", ()
});
COMMANDF(millis, "", () { intret(totalmillis); });
COMMANDN(setclipboardtext, SDL_SetClipboardText, "s");
+9
View File
@@ -1636,6 +1636,13 @@ void whois(int *cn)
}
COMMAND(whois, "i");
void getvita(int *cn)
{
addmsg(SV_GETVITA, "ri", *cn);
}
COMMAND(getvita, "i");
void findcn(char *name)
{
loopv(players) if(players[i] && !strcmp(name, players[i]->name))
@@ -1659,6 +1666,8 @@ void setadmin(int *claim, char *password)
}
else if(*claim != 0 && *password)
addmsg(SV_SETADMIN, "ris", *claim, genpwdhash(player1->name, password, sessionid));
else if(*claim != 0)
addmsg(SV_SETADMIN, "ri", *claim);
}
COMMAND(setadmin, "is");
+33
View File
@@ -1354,6 +1354,39 @@ void parsemessages(int cn, playerent *d, ucharbuf &p, bool demo = false)
getip4(p);
p.get((uchar*)text, 64);
break;
case SV_VITADATA: { // Server sending vita data of a client. Possibly expose this to cubescript in the future
uchar pubkey[32];
char pubkeyhex[68];
string privatecomment;
string publiccomment;
int vs[VS_NUM];
int targetcn = getint(p);
playerent *target = getclient(targetcn);
if (target && p.get(pubkey, 32) == 32) {
getstring(text, p);
filtertext(privatecomment, text, FTXT__VITACOMMENT);
getstring(text, p);
filtertext(publiccomment, text, FTXT__VITACOMMENT);
loopi(VS_NUM) vs[i] = getint(p);
bin2hex(pubkeyhex, pubkey, 32);
conoutf("\f0Vita Info for \f5%s \f0(\f5%d\f0) \f2pubkey: \f1%s", target->name, targetcn, pubkeyhex);
if(privatecomment[0]) conoutf("\f0Private Comment: \f1%s", privatecomment);
if(publiccomment[0]) conoutf("\f0Public Comment: \f1%s", publiccomment);
string msg = "\f0Flags:";
for (int i = 0; i < VS_NUM; i++) {
concatformatstring(msg, " \f2%s=\f1%d", vskeywords[i], vs[i]);
}
conoutf("%s", msg);
}
break;
}
default:
neterr("type");
+77 -30
View File
@@ -377,7 +377,7 @@ void popMT() // undo last seedMT()
#define ENTCHUNKS 32 // pool size (in 128-bytes chunks)
#define ENTPOOLSIZE (ENTCHUNKS * 128)
#define ENTROPYSAVEFILE "config" PATHDIVS "entropy.dat"
#define ENTROPYSAVEFILE "private" PATHDIVS "entropy.dat"
static uchar *entpool = NULL;
@@ -719,8 +719,8 @@ COMMAND(ed25519speedtest, "");
// * it is used during playing to authenticate the player to the server
// * needs special protection...
#define AUTHPREPRIVATECFGFILE "config" PATHDIVS "authpreprivate.cfg"
#define AUTHPRIVATECFGFILE "config" PATHDIVS "authprivate.cfg"
#define AUTHPREPRIVATECFGFILE "private" PATHDIVS "authpreprivate.cfg"
#define AUTHPRIVATECFGFILE "private" PATHDIVS "authprivate.cfg"
VARP(authmemusage, 2, 24, (1<<10) - 1); // megabytes of RAM to use for password hash (when using a new password)
VARP(authrounds, 0, 0, INT_MAX - 1); // create new password hashes with a fixed number of rounds (if authrounds > 0)
@@ -741,6 +741,63 @@ static int passdeferred(void *pass) // decrypt the private key in the backgrou
return 0;
}
void savepreprivkey(const char *filename, uchar *preprivkey, uchar preprivlen, uchar *psalt, uint preprivpwdcfg) {
char hextemp[128 * 2 + 1];
char *oldfile = loadfile(filename, NULL), *b;
stream *f = openfile(filename, "wb");
if(f)
{
if(!oldfile)
{
f->printf("\n" "// remove this file from your computer immediately!\n"
"// either print it out and delete it or move it to a thumbdrive.\n"
"// YOU DO NOT NEED THIS FILE TO PLAY AC!\n" "\n");
}
else
{
for(char *l = strtok_r(oldfile, "\n\r", &b); l; l = strtok_r(NULL, "\n\r", &b))
{
if(*l && l[0] != '/')
f->printf("// %s\n", l);
if(*l && l[0] == '/')
f->printf("%s\n", l);
}
}
f->printf("authsetup pre %s", bin2hex(hextemp, preprivkey, preprivlen));
if(preprivpwdcfg) f->printf(" %s %u", bin2hex(hextemp, psalt, 16), preprivpwdcfg & ~1);
f->printf("\n\n");
delete f;
}
DELETEA(oldfile);
}
void saveprivkey(const char *filename, uchar *privkey, uchar *pubkey, uchar salt[16], uint privpwdcfg) {
char hextemp[65];
char *oldfile = loadfile(filename, NULL), *b;
stream *f = openfile(filename, "wb");
if(f)
{
if(oldfile)
{
for(char *l = strtok_r(oldfile, "\n\r", &b); l; l = strtok_r(NULL, "\n\r", &b))
{
if(*l && l[0] != '/')
f->printf("// %s\n", l);
if(*l && l[0] == '/')
f->printf("%s\n", l);
}
}
f->printf("\nauthsetup priv %s", bin2hex(hextemp, privkey, 32));
if(privpwdcfg) f->printf(" %s %u", bin2hex(hextemp, salt, 16), privpwdcfg);
f->printf("\nauthsetup pub %s\n\n", bin2hex(hextemp, pubkey, 32));
delete f;
}
DELETEA(oldfile);
}
void authsetup(char **args, int numargs) // set up private and public keys
{
const int preprivminlen = 32, preprivmaxlen = 128;
@@ -865,22 +922,10 @@ void authsetup(char **args, int numargs) // set up private and public keys
preprivpwdcfg = (iterations << 10) | authmemusage;
xor_block(prepriv, keyhash, preprivlen);
}
const char *fn = numargs > 2 && args[2][0] ? path(args[2]) : AUTHPREPRIVATECFGFILE;
char *oldfile = loadfile(fn, NULL), *b;
stream *f = openfile(fn, "wb");
if(f)
{
f->printf("\n" "// remove this file from your computer immediately!\n"
"// either print it out and delete it or move it to a thumbdrive.\n"
"// YOU DO NOT NEED THIS FILE TO PLAY AC!\n" "\n");
if(oldfile) for(char *l = strtok_r(oldfile, "\n\r", &b); l; l = strtok_r(NULL, "\n\r", &b)) if(*l) f->printf("// %s\n", l);
f->printf("authsetup pre %s", bin2hex(hextemp, prepriv, preprivlen));
if(preprivpwdcfg) f->printf(" %s %u", bin2hex(hextemp, psalt, 16), preprivpwdcfg & ~1);
f->printf("\n\n");
delete f;
}
DELETEA(oldfile);
}
savepreprivkey(numargs > 2 && args[2][0] ? path(args[2]) : AUTHPREPRIVATECFGFILE,
prepriv, preprivlen, psalt, preprivpwdcfg);
}
else if(!strcasecmp(args[0], "NEWPASS"))
{
@@ -896,20 +941,21 @@ void authsetup(char **args, int numargs) // set up private and public keys
privpwdcfg = (iterations << 10) | authmemusage;
xor_block(priv, keyhash, 32);
}
const char *fn = numargs > 2 && args[2][0] ? path(args[2]) : AUTHPRIVATECFGFILE;
char *oldfile = loadfile(fn, NULL), *b;
stream *f = openfile(fn, "wb");
if(f)
{
if(oldfile) for(char *l = strtok_r(oldfile, "\n\r", &b); l; l = strtok_r(NULL, "\n\r", &b)) if(*l) f->printf("// %s\n", l);
f->printf("\nauthsetup priv %s", bin2hex(hextemp, priv, 32));
if(privpwdcfg) f->printf(" %s %u", bin2hex(hextemp, salt, 16), privpwdcfg);
f->printf("\nauthsetup pub %s\n\n", bin2hex(hextemp, keyhash + 32, 32));
delete f;
}
DELETEA(oldfile);
saveprivkey(numargs > 2 && args[2][0] ? path(args[2]) : AUTHPRIVATECFGFILE,
priv, keyhash + 32, salt, privpwdcfg);
}
}
else if(!strcasecmp(args[0], "SAVEPRIV"))
{
ed25519_pubkey_from_private(keyhash + 32, priv);
saveprivkey(numargs > 1 && args[1][0] ? path(args[1]) : AUTHPRIVATECFGFILE,
priv, keyhash + 32, salt, privpwdcfg);
}
else if(!strcasecmp(args[0], "SAVEPRE"))
{
savepreprivkey(numargs > 1 && args[1][0] ? path(args[1 ]) : AUTHPREPRIVATECFGFILE,
prepriv, preprivlen, psalt, preprivpwdcfg);
}
#ifndef PRODUCTION
else if(!strcasecmp(args[0], "UNARMED")) // FIXME: this is for testing purposes only - to test simultaneous logins from the same account - delete before release!
{
@@ -935,6 +981,7 @@ void authsetup(char **args, int numargs) // set up private and public keys
}
COMMAND(authsetup, "v");
void mypubkey()
{
string res = "";
+1
View File
@@ -605,6 +605,7 @@ static const int msgsizes[] = // size inclusive message token, 0 f
SV_EXTENSION, 0,
SV_MAPIDENT, 3, SV_DEMOCHECKSUM, 0, SV_DEMOSIGNATURE, 0,
SV_PAUSEMODE, 1,
SV_GETVITA, 2, SV_VITADATA, 0,
-1
};
+3 -2
View File
@@ -7,7 +7,7 @@
#define CUBE_SERVINFO_PORT(serverport) (serverport+1)
#define CUBE_SERVINFO_TO_SERV_PORT(servinfoport) (servinfoport-1)
#define CLIENTIPCENSOR 12 // blank out the last 12 bits of client's IPs before telling them to others (compromise between "16" from AC 0.9x and "8" from AC 1.1)
#define PROTOCOL_VERSION 1203 // bump when protocol changes (use negative numbers for mods!)
#define PROTOCOL_VERSION 1204 // bump when protocol changes (use negative numbers for mods!)
#define DEMO_VERSION 2 // bump when demo format changes
#define DEMO_MAGIC "ASSAULTCUBE_DEMO"
#define DEMO_MINTIME 10000 // don't keep demo recordings with less than 10 seconds
@@ -50,6 +50,7 @@ enum
SV_EXTENSION,
SV_MAPIDENT, SV_DEMOCHECKSUM, SV_DEMOSIGNATURE,
SV_PAUSEMODE,
SV_GETVITA, SV_VITADATA,
SV_NUM
};
@@ -86,7 +87,7 @@ enum { DISC_PROTOCOL = 0, DISC_EOP, DISC_CN, DISC_TAGT, DISC_DUP, DISC_OVERFLOW,
DISC_REMOVED = 32, DISC_SOPLOGINFAIL, DISC_BADNICK, DISC_ANNOY, DISC_SPAM, DISC_DOS, DISC_SPEEDING, DISC_FISHSLAP, DISC_AFK, // disconnected
DISC_KICK = 48, DISC_BAN, DISC_VOTEKICK, DISC_VOTEBAN, DISC_OPKICK, DISC_OPBAN, DISC_TKKICK, DISC_TKBAN, DISC_FFKICK, DISC_FFBAN, DISC_AUTOKICK, DISC_AUTOBAN,
DISC_UNKNOWN = 99, DISC_BECAUSE };
enum { BAN_NONE = 0, BAN_VOTE, BAN_AUTO, BAN_BLACKLIST, BAN_MASTER };
enum { BAN_NONE = 0, BAN_VOTE, BAN_AUTO, BAN_BLACKLIST, BAN_VITA, BAN_MASTER };
#define EXT_ACK -1
#define EXT_VERSION 104
+3 -2
View File
@@ -1026,8 +1026,9 @@ extern void dumpexecutionstack(stream *f);
extern const char *currentserver(int i);
// server
enum { /* timecodes */ VS_FIRSTLOGIN = 0, VS_LASTLOGIN, VS_MUTE, VS_NOVOTE, VS_BAN, VS_ADMIN, VS_OWNER, VS_MAPUPLOAD,
/* stats/counters */ VS_MINUTESCONNECTED, VS_MINUTESACTIVE, VS_FLAGS, VS_FRAGS, VS_DEATHS, VS_TKS, VS_DAMAGE, VS_FF, VS_NUM, VS_NUMCOUNTERS = VS_MINUTESCONNECTED };
enum { /* timecodes */ VS_FIRSTLOGIN = 0, VS_LASTLOGIN, VS_BAN, VS_WHITELISTED, VS_ADMIN, VS_OWNER,
/* stats/counters */ VS_MINUTESCONNECTED, VS_MINUTESACTIVE, VS_FLAGS, VS_ANTIFLAGS, VS_FRAGS, VS_DEATHS, VS_TKS, VS_SUICIDES, VS_DAMAGE, VS_FRIENDLYDAMAGE,
/* meta */ VS_NUM, VS_NUMCOUNTERS = VS_MINUTESCONNECTED };
extern const char *vskeywords[VS_NUM + 1], *vsnames[VS_NUM + 1];
extern int modeacronyms;
extern void servertoclient(int chan, uchar *buf, int len, bool demo = false);
+102 -10
View File
@@ -241,7 +241,10 @@ void poll_serverthreads() // called once per mainloop-timeslice
int too_old = 0;
enumeratekt(vitas, uchar32, k, vita_s, v,
{
if((servclock - v.vs[VS_LASTLOGIN]) / (60 * 24 * 31) <= vitamaxage)
if((servclock - v.vs[VS_LASTLOGIN]) / (60 * 24 * 31) <= vitamaxage // Vita is within the max age (months)
|| (v.vs[VS_BAN] == 1 || (servclock - v.vs[VS_BAN]) < 0) // Do not remove if vita has the ban, whitelist, or admin flag set
|| (v.vs[VS_WHITELISTED] == 1 || (servclock - v.vs[VS_WHITELISTED]) < 0)
|| (v.vs[VS_ADMIN] == 1 || (servclock - v.vs[VS_ADMIN]) < 0))
{
vk.k = &k;
vk.v = &v;
@@ -1437,6 +1440,7 @@ void flagaction(int flag, int action, int actor)
{
client *c = clients[actor];
c->state.flagscore += score;
c->incrementvitacounter(VS_FLAGS, score);
sendf(-1, 1, "riii", SV_FLAGCNT, actor, c->state.flagscore);
}
if(valid_client(actor))
@@ -1875,7 +1879,18 @@ void serverdamage(client *target, client *actor, int damage, int gun, bool gib,
if(damage < INT_MAX)
{
actor->state.damage += damage;
sendf(-1, 1, "ri7", gib ? SV_GIBDAMAGE : SV_DAMAGE, target->clientnum, actor->clientnum, gun, damage, ts.armour, ts.health);
if (!isteam(target->team, actor->team))
{
actor->incrementvitacounter(VS_DAMAGE, damage);
}
else
{
actor->incrementvitacounter(VS_FRIENDLYDAMAGE, damage);
}
if(target!=actor)
{
if(!hitpush.iszero())
@@ -1892,19 +1907,27 @@ void serverdamage(client *target, client *actor, int damage, int gun, bool gib,
int targethasflag = clienthasflag(target->clientnum);
bool tk = false, suic = false;
target->state.deaths++;
target->incrementvitacounter(VS_DEATHS, 1);
if(target!=actor)
{
if(!isteam(target->team, actor->team)) actor->state.frags += gib && gun != GUN_GRENADE && gun != GUN_SHOTGUN ? 2 : 1;
if(!isteam(target->team, actor->team))
{
actor->state.frags += gib && gun != GUN_GRENADE && gun != GUN_SHOTGUN ? 2 : 1;
actor->incrementvitacounter(VS_FRAGS, 1);
}
else
{
actor->state.frags--;
actor->state.teamkills++;
actor->incrementvitacounter(VS_TKS, 1);
tk = true;
}
}
else
{ // suicide
actor->state.frags--;
actor->incrementvitacounter(VS_TKS, 1);
suic = true;
mlog(ACLOG_INFO, "[%s] %s suicided", actor->hostname, actor->name);
}
@@ -1912,6 +1935,7 @@ void serverdamage(client *target, client *actor, int damage, int gun, bool gib,
if((suic || tk) && (m_htf || m_ktf) && targethasflag >= 0)
{
actor->state.flagscore--;
actor->incrementvitacounter(VS_ANTIFLAGS, 1);
sendf(-1, 1, "riii", SV_FLAGCNT, actor->clientnum, actor->state.flagscore);
}
target->position.setsize(0);
@@ -2404,6 +2428,7 @@ int getbantype(int cn)
if(!valid_client(cn)) return BAN_NONE;
client &c = *clients[cn];
if(c.type==ST_LOCAL) return BAN_NONE;
if(c.checkvitadate(VS_BAN)) return BAN_VITA;
if(checkgban(c.peer->address.host)) return BAN_MASTER;
if(ipblacklist.check(c.peer->address.host)) return BAN_BLACKLIST;
loopv(bans)
@@ -2623,23 +2648,38 @@ void callvotepacket (int cn, voteinfo *v = curvote)
sendpacket(cn, 1, q.finalize());
}
// TODO: use AUTH code
void changeclientrole(int client, int role, char *pwd, bool force)
{
pwddetail pd;
bool success = false;
if(!isdedicated || !valid_client(client)) return;
pd.line = -1;
if(force || role == CR_DEFAULT || (role == CR_ADMIN && pwd && pwd[0] && passwords.check(clients[client]->name, pwd, clients[client]->salt, &pd) && !pd.denyadmin))
{
if(role == clients[client]->role) return;
if(role == clients[client]->role) return;
if(force) success = true;
if(role == CR_ADMIN
&& pwd
&& pwd[0]
&& passwords.check(clients[client]->name, pwd, clients[client]->salt, &pd)
&& !pd.denyadmin) {
mlog(ACLOG_INFO,"[%s] player %s used admin password in line %d", clients[client]->hostname, clients[client]->name[0] ? clients[client]->name : "[unnamed]", pd.line);
success = true;
}
if(clients[client]->checkvitadate(VS_ADMIN)) {
mlog(ACLOG_INFO,"[%s] player %s claimed admin through valid vita claim (pubkey: %s)", clients[client]->hostname, clients[client]->name[0] ? clients[client]->name : "[unnamed]", clients[client]->pubkeyhex);
success = true;
}
if(success) {
if(role > CR_DEFAULT)
{
loopv(clients) clients[i]->role = CR_DEFAULT;
}
clients[client]->role = role;
sendserveropinfo(-1);
if(pd.line > -1)
mlog(ACLOG_INFO,"[%s] player %s used admin password in line %d", clients[client]->hostname, clients[client]->name[0] ? clients[client]->name : "[unnamed]", pd.line);
mlog(ACLOG_INFO,"[%s] set role of player %s to %s", clients[client]->hostname, clients[client]->name[0] ? clients[client]->name : "[unnamed]", role == CR_ADMIN ? "admin" : "normal player"); // flowtron : connecting players haven't got a name yet (connectadmin)
if(role > CR_DEFAULT) sendiplist(client);
}
@@ -3078,9 +3118,19 @@ void process(ENetPacket *packet, int sender, int chan)
bool srvfull = numnonlocalclients() > scl.maxclients;
bool srvprivate = sg->mastermode == MM_PRIVATE || sg->mastermode == MM_MATCH;
bool matchreconnect = sg->mastermode == MM_MATCH && findscore(*cl, false);
if (cl->pubkeyhex[0]) {
concatformatstring(tags, ", pubkey: %s", cl->pubkeyhex);
}
int bl = 0, wl = nickblacklist.checkwhitelist(*cl);
if(wl == NWL_PASS) concatstring(tags, ", nickname whitelist match");
if(wl == NWL_UNLISTED) bl = nickblacklist.checkblacklist(cl->name);
bool vitawhitelist = cl->checkvitadate(VS_WHITELISTED);
if (vitawhitelist) {
concatstring(tags, ", vita whitelist match");
banned = false;
}
if(matchreconnect && !banned)
{ // former player reconnecting to a server in match mode
cl->isauthed = true;
@@ -3114,6 +3164,24 @@ void process(ENetPacket *packet, int sender, int chan)
}
mlog(ACLOG_INFO, "[%s] %s logged in using the admin password in line %d%s", cl->hostname, cl->name, pd.line, tags);
}
else if(wantrole == CR_ADMIN && cl->checkvitadate(VS_ADMIN) && bantype != BAN_MASTER) // pass admins always through
{ // Set as admin in vita and is trying to connect as admin
cl->isauthed = true;
clientrole = CR_ADMIN;
if(bantype == BAN_VOTE)
{
loopv(bans) if(bans[i].address.host == cl->peer->address.host) { bans.remove(i); concatstring(tags, ", ban removed"); break; } // remove admin bans
}
if(srvfull)
{
loopv(clients) if(i != sender && clients[i]->type==ST_TCPIP)
{
disconnect_client(i, DISC_MAXCLIENTS); // disconnect someone else to fit maxclients again
break;
}
}
mlog(ACLOG_INFO, "[%s] %s logged in using the vita admin claim%s", cl->hostname, cl->name, tags);
}
else if(scl.serverpassword[0] && !(srvprivate || srvfull || banned))
{ // server password required
if(!strcmp(genpwdhash(cl->name, scl.serverpassword, cl->salt), cl->pwd))
@@ -3974,6 +4042,30 @@ void process(ENetPacket *packet, int sender, int chan)
case SV_PAUSEMODE:
break;
case SV_GETVITA:
{
int targetcn = getint(p);
// If the client is admin or could be admin through vita
if (!(cl->role >= CR_ADMIN || cl->checkvitadate(VS_ADMIN) || cl->checkvitadate(VS_OWNER))) {
sendservmsg("\f3You must be admin to get vita");
break;
}
if (valid_client(targetcn)) {
client *target = clients[targetcn];
if (target->vita) {
sendf(sender, 1, "riimssv", SV_VITADATA, targetcn, 32, target->pubkey.u,
target->vita->privatecomment, target->vita->publiccomment,
VS_NUM, target->vita->vs);
}
} else {
sendservmsg("\f3Invalid cn to retrieve vita data");
}
break;
}
case SV_EXTENSION:
{
@@ -4425,8 +4517,8 @@ void serverslice(uint timeout) // main server update, called from cube main lo
serverhost->totalSentData = serverhost->totalReceivedData = 0;
loopv(clients) if(clients[i]->type == ST_TCPIP && clients[i]->isauthed && clients[i]->vita)
{
clients[i]->vita->vs[VS_MINUTESCONNECTED]++;
if(clients[i]->isonrightmap && team_isactive(clients[i]->team)) clients[i]->vita->vs[VS_MINUTESACTIVE]++;
clients[i]->incrementvitacounter(VS_MINUTESCONNECTED, 1);
if(clients[i]->isonrightmap && team_isactive(clients[i]->team)) clients[i]->incrementvitacounter(VS_MINUTESACTIVE, 1);
}
}
+6
View File
@@ -353,6 +353,12 @@ struct client // server side version of "dynent" type
extern int servclock;
return vita && (vita->vs[n] == 1 || (servclock - vita->vs[n]) < 0);
}
void incrementvitacounter(int vitastat, int amount)
{
if (!vita) return;
vita->vs[vitastat] += amount;
}
};
struct ban
+3 -3
View File
@@ -1983,9 +1983,9 @@ void serverparameters_dumpdocu(char *fname)
// player ID bookkeeping
const char *vskeywords[VS_NUM + 1] = { "first", "last", "mute", "novote", "ban", "admin", "owner", "mapupload", "minconn", "minact", "flags", "frags", "deaths", "tks", "damage", "ff", "" };
const char *vsnames[VS_NUM + 1] = { "first login", "last login", "muted until", "novote until", "banned until", "admin until", "owner until", "mapupload until",
"minutes connected", "minutes active", "flags", "frags", "deaths", "teamkills", "damage dealt", "team damage dealt", "" };
const char *vskeywords[VS_NUM + 1] = { "first", "last", "ban", "whitelist", "admin", "owner", "minconn", "minact", "flags", "antiflags", "frags", "deaths", "tks", "suicides", "damage", "ff", "" };
const char *vsnames[VS_NUM + 1] = { "first login", "last login", "banned until", "whitelisted untils", "admin until", "owner until",
"minutes connected", "minutes active", "flags", "flag score lost", "frags", "deaths", "teamkills", "suicides", "damage dealt", "team damage dealt", "" };
hashtable<uchar32, vita_s> vitas;
void vita_s::addname(const char *name)