diff options
| author | Darkelarious <darkelarious@333networks.com> | 2015-11-08 22:29:50 +0100 |
|---|---|---|
| committer | Darkelarious <darkelarious@333networks.com> | 2015-11-08 22:29:50 +0100 |
| commit | dc409b9cb6e92fd9ff9ef18ab2ca825bd247b419 (patch) | |
| tree | 7c4b6cc56d02012a58635abe0be987bbe7f28ec6 /lib | |
| parent | 8b3d393e7755c167eebe4d9f7fc786074f12e9af (diff) | |
| download | MasterServer-Perl-dc409b9cb6e92fd9ff9ef18ab2ca825bd247b419.tar.gz MasterServer-Perl-dc409b9cb6e92fd9ff9ef18ab2ca825bd247b419.zip | |
Access ciphers and games through database instead of keeping them in memory
Diffstat (limited to 'lib')
| -rwxr-xr-x | lib/MasterServer/Core/Core.pm | 3 | ||||
| -rwxr-xr-x | lib/MasterServer/Core/Secure.pm | 77 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/Pg/dbCiphers.pm | 98 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/SQLite/dbCiphers.pm | 98 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/mysql/dbCiphers.pm | 98 | ||||
| -rwxr-xr-x | lib/MasterServer/TCP/Handler.pm | 2 | ||||
| -rwxr-xr-x | lib/MasterServer/TCP/ListCompiler.pm | 2 | ||||
| -rwxr-xr-x | lib/MasterServer/TCP/Syncer.pm | 6 | ||||
| -rwxr-xr-x | lib/MasterServer/UDP/DatagramProcessor.pm | 21 |
9 files changed, 382 insertions, 23 deletions
diff --git a/lib/MasterServer/Core/Core.pm b/lib/MasterServer/Core/Core.pm index 4cd6063..a20172c 100755 --- a/lib/MasterServer/Core/Core.pm +++ b/lib/MasterServer/Core/Core.pm @@ -82,6 +82,9 @@ sub main { } } + # (re)load the list with ciphers from the config file, into the database + $self->load_ciphers(); + # start the listening service (listen for UDP beacons) $self->{scope}->{beacon_catcher} = $self->beacon_catcher(); diff --git a/lib/MasterServer/Core/Secure.pm b/lib/MasterServer/Core/Secure.pm index 3b85498..07e48b8 100755 --- a/lib/MasterServer/Core/Secure.pm +++ b/lib/MasterServer/Core/Secure.pm @@ -6,7 +6,59 @@ use warnings; use POSIX qw/strftime/; use Exporter 'import'; -our @EXPORT = qw| secure_string validated_beacon validated_request validate_string charshift get_validate_string|; +our @EXPORT = qw| load_ciphers + secure_string + validated_beacon + validated_request + validate_string + charshift + get_validate_string |; + +################################################################################ +## Supported Games list ciphers +## Clear the Supported Games table and insert the list of supported games AND +## their ciphers / default ports / descriptions included from the +## data/supportedgames.pl file. +## +## Only config files after 5 October 2015 work with this script. +## IMPORTANT! Afterwards, the $self->{game} hash will be cleared! +################################################################################ +sub load_ciphers { + my $self = shift; + + # first delete the old cipher database + $self->clear_ciphers(); + + # start inserting ciphers (lots of 'em) + $self->{dbh}->begin_work; + + # iterate through the game list + for (keys %{$self->{game}}) { + + # verify entries + my %opt = (); + $opt{gamename} = lc $_; + $opt{cipher} = $self->{game}->{$_}->{key}; + $opt{description} = $self->{game}->{$_}->{label} || 'Unknown Game'; + $opt{default_qport} = $self->{game}->{$_}->{port} || 0; + + # insert the game/cipher in the db or halt on error + if ($self->insert_cipher(%opt) < 0) { + $self->{dbh}->rollback; + $self->halt(); + } + + } + + # commit + $self->{dbh}->commit; + $self->log("info", "Cipher database successfully updated!"); + + # unload the game variables from memory + $self->{game} = undef; + +} + ################################################################################ # generate a random string of 6 characters long for the \secure\ challenge @@ -75,7 +127,7 @@ sub validate_string { my ($self, $game, $sec, $enc) = @_; # get cipher from gamename - my $cip = $self->{game}->{$game}->{key} || "000000"; + my $cip = $self->get_cipher($game); # don't accept challenge longer than 16 characters (because vulnerable in UE) if (length $sec > 16) { @@ -123,9 +175,26 @@ sub charshift { sub get_validate_string { my ($self, $cipher_string, $secure_string, $enctype) = @_; - # import pre-built rotations from config for enctype + # use pre-built rotations for enctype # -- see GSMSALG 0.3.3 reference for copyright and more information - my @enc_chars = $self->{enc_chars}; + my @enc_chars = ( qw | + 001 186 250 178 081 000 084 128 117 022 142 142 002 008 054 165 + 045 005 013 022 082 007 180 034 140 233 009 214 185 038 000 004 + 006 005 000 019 024 196 030 091 029 118 116 252 080 081 006 022 + 000 081 040 000 004 010 041 120 081 000 001 017 082 022 006 074 + 032 132 001 162 030 022 071 022 050 081 154 196 003 042 115 225 + 045 079 024 075 147 076 015 057 010 000 004 192 018 012 154 094 + 002 179 024 184 007 012 205 033 005 192 169 065 067 004 060 082 + 117 236 152 128 029 008 002 029 088 132 001 078 059 106 083 122 + 085 086 087 030 127 236 184 173 000 112 031 130 216 252 151 139 + 240 131 254 014 118 003 190 057 041 119 048 224 043 255 183 158 + 001 004 248 001 014 232 083 255 148 012 178 069 158 010 199 006 + 024 001 100 176 003 152 001 235 002 176 001 180 018 073 007 031 + 095 094 093 160 079 091 160 090 089 088 207 082 084 208 184 052 + 002 252 014 066 041 184 218 000 186 177 240 018 253 035 174 182 + 069 169 187 006 184 136 020 036 169 000 020 203 036 018 174 204 + 087 086 238 253 008 048 217 253 139 062 010 132 070 250 119 184 + |), # convert to array of characters my @cip = split "", $cipher_string; diff --git a/lib/MasterServer/Database/Pg/dbCiphers.pm b/lib/MasterServer/Database/Pg/dbCiphers.pm new file mode 100755 index 0000000..e2e8191 --- /dev/null +++ b/lib/MasterServer/Database/Pg/dbCiphers.pm @@ -0,0 +1,98 @@ + +package MasterServer::Database::Pg::dbCiphers; + +use strict; +use warnings; +use Exporter 'import'; + +our @EXPORT = qw| clear_ciphers + insert_cipher + get_cipher + get_default_port |; + +################################################################################ +## Clear all existing ciphers from the database +################################################################################ +sub clear_ciphers { + my $self = shift; + + # delete ALL entries + my $u = $self->{dbh}->do("DELETE FROM games"); + + # notify + $self->log("delete", "Removed all ciphers") if ($u > 0); + + # removed from games + return 2 if ($u > 0); + + # or else report notice + $self->log("notice", "No ciphers deleted!"); + return -1; + +} + +################################################################################ +## Insert the list of supported games and their ciphers / default ports / +## descriptions included from the data/supportedgames.pl file. +################################################################################ +sub insert_cipher { + my ($self, %opt) = @_; + + # insert a single cipher/key combo + my $u = $self->{dbh}->do( + "INSERT INTO games ( + gamename, + cipher, + description, + default_qport) + VALUES(?, ?, ?, ?)", undef, + $opt{gamename}, $opt{cipher}, $opt{description}, $opt{default_qport}); + + # notify + $self->log("add", "Added cipher for $opt{gamename}") if ($u and $u > 0); + return 1 if ($u and $u > 0); + + # or else report error + $self->log("error", "An error occurred adding a cipher for $opt{gamename}"); + return -1; + +} + + +################################################################################ +## get the cipher that goes with gamename +################################################################################ +sub get_cipher { + my ($self, $gn) = @_; + + # no gamename specified? "undef" is not a known cipher, so send that instead. + return 'undef' if !$gn; + + # get cipher from db if gamename exists + my $cipher = $self->{dbh}->selectall_arrayref( + 'SELECT cipher FROM games WHERE gamename = ?', undef, + lc $gn)->[0]->[0]; + + # return a non-zero-length string + return ($cipher ? $cipher : 'undef'); +} + +################################################################################ +## get the default query port that goes with gamename +################################################################################ +sub get_default_port { + my ($self, $gn) = @_; + + # no gamename specified? default port is 0 + return 0 if !$gn; + + # get port from db if gamename exists + my $p = $self->{dbh}->selectall_arrayref( + 'SELECT default_qport FROM games WHERE gamename = ?', undef, + lc $gn)->[0]->[0]; + + # return port or zero + return $p || 0; +} + +1; diff --git a/lib/MasterServer/Database/SQLite/dbCiphers.pm b/lib/MasterServer/Database/SQLite/dbCiphers.pm new file mode 100755 index 0000000..0dc388d --- /dev/null +++ b/lib/MasterServer/Database/SQLite/dbCiphers.pm @@ -0,0 +1,98 @@ + +package MasterServer::Database::SQLite::dbCiphers; + +use strict; +use warnings; +use Exporter 'import'; + +our @EXPORT = qw| clear_ciphers + insert_cipher + get_cipher + get_default_port |; + +################################################################################ +## Clear all existing ciphers from the database +################################################################################ +sub clear_ciphers { + my $self = shift; + + # delete ALL entries + my $u = $self->{dbh}->do("DELETE FROM games"); + + # notify + $self->log("delete", "Removed all ciphers") if ($u > 0); + + # removed from games + return 2 if ($u > 0); + + # or else report notice + $self->log("notice", "No ciphers deleted!"); + return -1; + +} + +################################################################################ +## Insert the list of supported games and their ciphers / default ports / +## descriptions included from the data/supportedgames.pl file. +################################################################################ +sub insert_cipher { + my ($self, %opt) = @_; + + # insert a single cipher/key combo + my $u = $self->{dbh}->do( + "INSERT INTO games ( + gamename, + cipher, + description, + default_qport) + VALUES(?, ?, ?, ?)", undef, + $opt{gamename}, $opt{cipher}, $opt{description}, $opt{default_qport}); + + # notify + $self->log("add", "Added cipher for $opt{gamename}") if ($u and $u > 0); + return 1 if ($u and $u > 0); + + # or else report error + $self->log("error", "An error occurred adding a cipher for $opt{gamename}"); + return -1; + +} + + +################################################################################ +## get the cipher that goes with gamename +################################################################################ +sub get_cipher { + my ($self, $gn) = @_; + + # no gamename specified? "undef" is not a known cipher, so send that instead. + return 'undef' if !$gn; + + # get cipher from db if gamename exists + my $cipher = $self->{dbh}->selectall_arrayref( + 'SELECT cipher FROM games WHERE gamename = ?', undef, + lc $gn)->[0]->[0]; + + # return a non-zero-length string + return ($cipher ? $cipher : 'undef'); +} + +################################################################################ +## get the default query port that goes with gamename +################################################################################ +sub get_default_port { + my ($self, $gn) = @_; + + # no gamename specified? default port is 0 + return 0 if !$gn; + + # get port from db if gamename exists + my $p = $self->{dbh}->selectall_arrayref( + 'SELECT default_qport FROM games WHERE gamename = ?', undef, + lc $gn)->[0]->[0]; + + # return port or zero + return $p || 0; +} + +1; diff --git a/lib/MasterServer/Database/mysql/dbCiphers.pm b/lib/MasterServer/Database/mysql/dbCiphers.pm new file mode 100755 index 0000000..31db78b --- /dev/null +++ b/lib/MasterServer/Database/mysql/dbCiphers.pm @@ -0,0 +1,98 @@ + +package MasterServer::Database::mysql::dbCiphers; + +use strict; +use warnings; +use Exporter 'import'; + +our @EXPORT = qw| clear_ciphers + insert_cipher + get_cipher + get_default_port |; + +################################################################################ +## Clear all existing ciphers from the database +################################################################################ +sub clear_ciphers { + my $self = shift; + + # delete ALL entries + my $u = $self->{dbh}->do("DELETE FROM games"); + + # notify + $self->log("delete", "Removed all ciphers") if ($u > 0); + + # removed from games + return 2 if ($u > 0); + + # or else report notice + $self->log("notice", "No ciphers deleted!"); + return -1; + +} + +################################################################################ +## Insert the list of supported games and their ciphers / default ports / +## descriptions included from the data/supportedgames.pl file. +################################################################################ +sub insert_cipher { + my ($self, %opt) = @_; + + # insert a single cipher/key combo + my $u = $self->{dbh}->do( + "INSERT INTO games ( + gamename, + cipher, + description, + default_qport) + VALUES(?, ?, ?, ?)", undef, + $opt{gamename}, $opt{cipher}, $opt{description}, $opt{default_qport}); + + # notify + $self->log("add", "Added cipher for $opt{gamename}") if ($u and $u > 0); + return 1 if ($u and $u > 0); + + # or else report error + $self->log("error", "An error occurred adding a cipher for $opt{gamename}"); + return -1; + +} + + +################################################################################ +## get the cipher that goes with gamename +################################################################################ +sub get_cipher { + my ($self, $gn) = @_; + + # no gamename specified? "undef" is not a known cipher, so send that instead. + return 'undef' if !$gn; + + # get cipher from db if gamename exists + my $cipher = $self->{dbh}->selectall_arrayref( + 'SELECT cipher FROM games WHERE gamename = ?', undef, + lc $gn)->[0]->[0]; + + # return a non-zero-length string + return ($cipher ? $cipher : 'undef'); +} + +################################################################################ +## get the default query port that goes with gamename +################################################################################ +sub get_default_port { + my ($self, $gn) = @_; + + # no gamename specified? default port is 0 + return 0 if !$gn; + + # get port from db if gamename exists + my $p = $self->{dbh}->selectall_arrayref( + 'SELECT default_qport FROM games WHERE gamename = ?', undef, + lc $gn)->[0]->[0]; + + # return port or zero + return $p || 0; +} + +1; diff --git a/lib/MasterServer/TCP/Handler.pm b/lib/MasterServer/TCP/Handler.pm index 9d05a32..2fb05f5 100755 --- a/lib/MasterServer/TCP/Handler.pm +++ b/lib/MasterServer/TCP/Handler.pm @@ -89,7 +89,7 @@ sub handle_validate { my $val = 0; # pass or fail the secure challenge - if (exists $r->{gamename} && exists $self->{game}->{$r->{gamename}}) { + if (exists $r->{gamename} && length $self->get_cipher(lc $r->{gamename}) > 1 ) { # game exists and we have the key to verify the response $val = $self->validated_request($r->{gamename}, $secure, $r->{enctype}, $r->{validate}); diff --git a/lib/MasterServer/TCP/ListCompiler.pm b/lib/MasterServer/TCP/ListCompiler.pm index 56bb256..1863757 100755 --- a/lib/MasterServer/TCP/ListCompiler.pm +++ b/lib/MasterServer/TCP/ListCompiler.pm @@ -93,7 +93,7 @@ sub compile_sync { for my $g (@games) { # $g is now a gamename -- check if it's supported. Else ignore. - if (exists $self->{game}->{$g}) { + if (length $self->get_cipher(lc $g) > 1) { # get list from database my $list = $self->get_game_list($g); diff --git a/lib/MasterServer/TCP/Syncer.pm b/lib/MasterServer/TCP/Syncer.pm index 69025f0..2ba52ca 100755 --- a/lib/MasterServer/TCP/Syncer.pm +++ b/lib/MasterServer/TCP/Syncer.pm @@ -126,10 +126,10 @@ sub process_sync_list { # iterate through the gamenames and addresses while ( my ($gn,$addr) = each %r) { - # only process gamenames that are in our list for supported games (supportedgames.pl) - if (defined $gn && exists $self->{game}->{lc $gn}) { + # process all games wether we have a cipher for them. + if (defined $gn) { - # database types such as SQLite are slow, therefore use transactions. + # some database types, such as SQLite, are slow - therefore use transactions. $self->{dbh}->begin_work; # l(ocations, \label\ip:port\) split up in a(ddress) and p(ort) diff --git a/lib/MasterServer/UDP/DatagramProcessor.pm b/lib/MasterServer/UDP/DatagramProcessor.pm index 632435c..f80f6fb 100755 --- a/lib/MasterServer/UDP/DatagramProcessor.pm +++ b/lib/MasterServer/UDP/DatagramProcessor.pm @@ -25,20 +25,14 @@ sub process_udp_beacon { my %r; $buf = encode('UTF-8', $buf); $buf =~ s/\\([^\\]+)\\([^\\]+)/$r{$1}=$2/eg; - - # check whether the beacon has a gamename that is supported in our list - if (defined $r{gamename} && exists $self->{game}->{lc $r{gamename}}) { + + # check whether the beacon has a gamename + if (defined $r{gamename}) { # log the beacon $self->log("beacon", "$peer_addr:$r{heartbeat} for $r{gamename}"); - # some games (like bcommander) have a default port and don't send a - # heartbeat port. - if ($r{heartbeat} == 0) { - # assuming a default port exists - if (exists $self->{game}->{lc $r{gamename}}->{port}) { - $r{heartbeat} = $self->{game}->{lc $r{gamename}}->{port}; - } - } + # some games (like bcommander) have a default port and don't send a heartbeat port. + $r{heartbeat} = $self->get_default_port($r{gamename}) if ($r{heartbeat} == 0); # # verify valid server address (ip+port) @@ -103,8 +97,7 @@ sub process_udp_validate { my $enc = (defined $r{enctype}) ? $r{enctype} : 0; # database may not contain the correct gamename (ucc applet, incomplete beacon, change of gameserver) - $pending->[4] = (defined $r{gamename} && exists $self->{game}->{lc $r{gamename}}) - ? $r{gamename} : $pending->[4]; + $pending->[4] = (defined $r{gamename}) ? $r{gamename} : $pending->[4]; # verify challenge gamename secure enctype validate_response my $val = $self->validated_beacon($pending->[4], $pending->[5], $enc, $r{validate}); @@ -147,7 +140,7 @@ sub process_query_response { $buf =~ s/\\([^\\]+)\\([^\\]+)/$s{$1}=$2/eg; # check whether the gamename is supported in our db - if (defined $s{gamename} && exists $self->{game}->{lc $s{gamename}}) { + if (defined $s{gamename} && length $self->get_cipher(lc $s{gamename}) > 1) { # parse variables my %nfo = (); |
