src/network/nKrawallPrivate.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 *************************************************************************
00004 
00005 ArmageTron -- Just another Tron Lightcycle Game in 3D.
00006 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
00007 
00008 **************************************************************************
00009 
00010 This program is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 as published by the Free Software Foundation; either version 2
00013 of the License, or (at your option) any later version.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00023 
00024 ***************************************************************************
00025 
00026 Although it is already included in the GPL, let me clarify:
00027 anybody, especially the Krawall Gaming network, is allowed
00028 to run a server/master server build from modified versions of
00029 this file WHITHOUT releasing the source to the public (provided
00030 the executable is not distributed).
00031 
00032 
00033 */
00034 
00035 #include "nKrawall.h"
00036 #include "tString.h"
00037 #include "tConsole.h"
00038 #include "nNetwork.h"
00039 #include "tConfiguration.h"
00040 #include "tArray.h"
00041 
00042 #include <string>
00043 #include <vector>
00044 #include <map>
00045 
00046 static nKrawall::nMethod sn_bmd5("bmd5"), sn_md5("md5");
00047 
00048 static tSettingItem< tString > sn_md5Prefix( "MD5_PREFIX", sn_md5.prefix );
00049 static tSettingItem< tString > sn_md5Suffix( "MD5_SUFFIX", sn_md5.suffix );
00050 
00052 nKrawall::nMethod const * const * nKrawall::nMethod::LocalMethods()
00053 {
00054     static nMethod const * methods[] =
00055     {
00056         &sn_md5,
00057         &sn_bmd5,
00058         NULL
00059     };
00060 
00061     return methods;
00062 }
00063 
00064 #ifdef KRAWALL_SERVER
00065 
00066 #include <libxml/nanohttp.h>
00067 
00068 // crude login structure with not too many feathres
00069 struct nLogin
00070 {
00071     tString authority_;
00072 
00073     // scrambled passwords according to our various methods
00074     std::map< tString, nKrawall::nScrambledPassword > scrambledPasswords_;
00075 
00076     // access level for the login
00077     tAccessLevel accessLevel_;
00078 
00079     // password for team accounts
00080     tString password_;
00081 
00082     static bool FixOK( tString const & fix )
00083     {
00084         return fix.StrPos( "%" ) < 0;
00085     }
00086 
00087     nLogin( char const * authority, tString const & password, tString const & username, tAccessLevel accessLevel )
00088     : authority_( authority ), accessLevel_( accessLevel )
00089     {
00090         // scramble the password before storing it (in case an evil attacker can read our memory,
00091         // but not the configuration file where they are stored in plain text :) )
00092         nKrawall::nMethod const * const * run = nKrawall::nMethod::LocalMethods();
00093         while ( * run )
00094         {
00095             if ( username.Len() <= 1 &&
00096                  ( !FixOK( (*run)->prefix ) || !FixOK( (*run)->suffix ) )
00097                 )
00098             {
00099                 // store plain text password
00100                 password_ = password;
00101             }
00102             else
00103             {
00104                 (*run)->ScramblePassword( nKrawall::nScrambleInfo( username ), password, scrambledPasswords_[ (*run)->method ] );
00105             }
00106 
00107             ++run;
00108         }
00109     }
00110 
00111     // check whether a scrambled password sent by a client is correct
00112     bool CheckPassword( nKrawall::nScrambleInfo const & info, nKrawall::nMethod const & method, nKrawall::nSalt const & salt, nKrawall::nScrambledPassword const & hash, tString & error ) const
00113     {
00114         // find the correct scrambled password
00115         std::map< tString, nKrawall::nScrambledPassword >::const_iterator scrambled = scrambledPasswords_.find( method.method );
00116         if ( scrambled != scrambledPasswords_.end() )
00117         {
00118             // check whether methods match exactly
00119             nKrawall::nMethod const * const * run = nKrawall::nMethod::LocalMethods();
00120             while ( * run )
00121             {
00122                 nKrawall::nMethod const & compare = **run;
00123                 if ( compare.method == method.method && !nKrawall::nMethod::Equal(compare, method ) )
00124                 {
00125                     error = tOutput( "$login_error_methodmismatch" );
00126                     return false;
00127                 }
00128 
00129                 ++run;
00130             }
00131 
00132             // compare passwords
00133             nKrawall::nScrambledPassword scrambledCorrect;
00134             method.ScrambleWithSalt( info, (*scrambled).second, salt, scrambledCorrect );
00135             bool ret = nKrawall::ArePasswordsEqual( hash, scrambledCorrect );
00136             if ( !ret )
00137             {
00138                 error = tOutput( "$login_error_local_password", info.username );
00139             }
00140             return ret;
00141         }
00142         else
00143         {
00144             if ( password_ == "" )
00145             {
00146                 error = "Internal error, local method not found, and no plaintext password stored.";
00147                 return false;
00148             }
00149 
00150             // do a full check on the stored password
00151             nKrawall::nScrambledPassword scrambled, correctHash;
00152             method.ScramblePassword( info, password_, scrambled );
00153             method.ScrambleWithSalt( info, scrambled, salt, correctHash );
00154 
00155             bool ret = nKrawall::ArePasswordsEqual( hash, correctHash );
00156             if ( !ret )
00157             {
00158                 error = tOutput( "$login_error_local_password", info.username );
00159             }
00160             return ret;
00161         }
00162 
00163         return false;
00164     }
00165 
00166     nLogin()
00167     {
00168     }
00169 };
00170 
00171 typedef std::map< tString, nLogin > nLoginMap;
00172 
00173 // database of exact logins
00174 static nLoginMap sn_exactLogins;
00175 
00176 // database of inprecise logins
00177 static nLoginMap sn_partialLogins;
00178 
00179 // add an admin account
00180 static void sn_ReadPassword( std::istream & s )
00181 {
00182     tString username, password;
00183     s >> username;
00184     if ( !s.good() )
00185     {
00186         con << tOutput( "$local_user_syntax" );
00187         return;
00188     }
00189     tConfItemBase::EatWhitespace(s);
00190     password.ReadLine(s);
00191     if ( password == "" )
00192     {
00193         con << tOutput( "$local_user_syntax" );
00194         return;
00195     }
00196 
00197     sn_exactLogins[ username ] = nLogin( "", password, username, tAccessLevel_Local );
00198 }
00199 
00200 static tConfItemFunc sn_kpa( "LOCAL_USER", sn_ReadPassword );
00201 static tAccessLevelSetter sn_kpal( sn_kpa, tAccessLevel_Owner );
00202 
00203 // add team account
00204 static void sn_ReadTeamPassword( std::istream & s )
00205 {
00206     tString username, password;
00207     s >> username;
00208     if ( !s.good() )
00209     {
00210         con << tOutput( "$local_team_syntax" );
00211         return;
00212     }
00213     tConfItemBase::EatWhitespace(s);
00214     password.ReadLine(s);
00215     if ( password == "" )
00216     {
00217         con << tOutput( "$local_team_syntax" );
00218         return;
00219     }
00220 
00221     sn_partialLogins[ username ] = nLogin( tString("L_TEAM_") + username, password, tString(""), tAccessLevel_TeamMember );
00222 }
00223 
00224 static tConfItemFunc sn_kta( "LOCAL_TEAM", sn_ReadTeamPassword );
00225 static tAccessLevelSetter sn_ktal( sn_kta, tAccessLevel_Owner );
00226 
00227 // finds a login element as iterator
00228 nLoginMap::iterator sn_FindLoginIterator( tString const & username, nLoginMap * & map, bool exact = false )
00229 {
00230     // find exact login
00231     map = &sn_exactLogins;
00232     nLoginMap::iterator found = sn_exactLogins.find( username );
00233     if ( found != sn_exactLogins.end() )
00234     {
00235         return found;
00236     }
00237 
00238     map = &sn_partialLogins;
00239     for( int i = username.Len(); i >= 1; --i )
00240     {
00241         tString partial = username.SubStr( 0, i );
00242         nLoginMap::iterator found = map->find( partial );
00243         if ( found != map->end() )
00244         {
00245             return found;
00246         }
00247 
00248         if ( exact )
00249         {
00250             return map->end();
00251         }
00252     }
00253 
00254     return map->end();
00255 }
00256 
00257 // finds login pointer
00258 nLogin const * sn_FindLogin( tString const & username )
00259 {
00260     // find login
00261     nLoginMap * map;
00262     nLoginMap::iterator found = sn_FindLoginIterator( username, map );
00263     if ( found != map->end() )
00264     {
00265         return &found->second;
00266     }
00267 
00268     return 0;
00269 }
00270 
00271 // remove an account
00272 static void sn_ReadPasswordRemove( std::istream & s )
00273 {
00274     tString username;
00275     s >> username;
00276     nLoginMap * map;
00277     nLoginMap::iterator found = sn_FindLoginIterator( username, map, true );
00278     if ( found != map->end() )
00279     {
00280         map->erase( found );
00281         con << tOutput( "$md5_password_removed", username );
00282     }
00283     else
00284     {
00285         con << tOutput( "$md5_password_remove_notfound", username );
00286     }
00287 }
00288 
00289 static tConfItemFunc sn_kpr( "USER_REMOVE", sn_ReadPasswordRemove );
00290 
00291 // fetch the scrambled password of username from the users database
00292 void nKrawall::CheckScrambledPassword( nCheckResultBase & result,
00293                                        nPasswordCheckData const & data )
00294 {
00295     // extra salt scrambling process
00296     nSalt salt = data.salt;
00297     data.method.ScrambleSalt( salt, data.serverAddress );
00298 
00299     // local users
00300     if ( result.authority.Len() <= 1 )
00301     {
00302         // fetch login data
00303         nLogin const * login = sn_FindLogin( result.username );
00304         if ( !login )
00305         {
00306             result.success = false;
00307             result.error = tOutput( "$login_error_local_nouser", result.username );
00308             return;
00309         }
00310 
00311         // store relevant authority
00312         result.authority = login->authority_;
00313 
00314         // check password
00315         result.success = login->CheckPassword( nScrambleInfo( result.username ), data.method, salt, data.hash, result.error );
00316         result.accessLevel = login->accessLevel_;
00317 
00318         return;
00319     }
00320     else
00321     {
00322         // remote users: build query URL
00323         std::ostringstream request;
00324 
00325         request << "?query=check";
00326         request << "&method=" << EncodeString( data.method.method );
00327         request << "&user="   << EncodeString( result.username );
00328         request << "&salt="   << EncodeScrambledPassword( salt );
00329         request << "&hash="   << EncodeScrambledPassword( data.hash );
00330 
00331         // read URL content
00332         std::stringstream content;
00333         int rc = FetchURL( data.fullAuthority, request.str().c_str(), content );
00334 
00335         if (rc == -1)
00336         {
00337             result.error = tOutput( "$login_error_invalidurl_notfound", result.authority );
00338             result.success = false;
00339             return;
00340         }
00341 
00342         // lots of string copying going on, probably should find a better way.
00343         char * buf_temp = strdup( content.str().c_str() );
00344         unsigned int len = strlen(buf_temp);
00345 
00346         // get rid of newlines
00347         for ( unsigned int i = 0; i < len; ++i )
00348         {
00349             if ( buf_temp[i] == '\n' )
00350             {
00351                 buf_temp[i] = ' ';
00352             }
00353         }
00354 
00355         // trailing spaces are ugly
00356         while ( len > 0 && buf_temp[len-1] == ' ' )
00357         {
00358             buf_temp[len-1] = 0;
00359             --len;
00360         }
00361 
00362         tString buf( buf_temp );
00363         free( buf_temp );
00364 
00365         // catch various error codes
00366         if ( rc != 200 ) {
00367             result.success = false;
00368             switch ( rc )
00369             {
00370             case 404:
00371                 result.error = tOutput( "$login_error_nouser", buf );
00372                 break;
00373             case 403:
00374             case 401:
00375                 result.error = tOutput( "$login_error_password", buf );
00376                 break;
00377             default:
00378                 result.error = tOutput( "$login_error_unknown", rc, buf );
00379                 break;
00380             }
00381 
00382             return;
00383         }
00384 
00385         // read the buffer
00386         tString ret;
00387         content >> ret;
00388         tToLower( ret );
00389 
00390         // catch the same errros frm the server's response
00391         if ( ret == "unknown_user" )
00392         {
00393             result.error = tOutput( "$login_error_nouser", buf );
00394             return;
00395         }
00396 
00397         if ( ret == "password_fail" )
00398         {
00399             result.error = tOutput( "$login_error_password", buf );
00400             return;
00401         }
00402 
00403         if ( ret != "password_ok" )
00404         {
00405             result.error << tOutput( "$login_error_unexpected_answer", "PASSWORD_OK ...", buf );
00406             return;
00407         }
00408 
00409         // everything fine so far. Read the full username returned from the authority.
00410         tString fullUserName;
00411         fullUserName.ReadLine( content );
00412 
00413         // check on it
00414         if ( fullUserName != "" )
00415         {
00416             tString claimedAuthority;
00417             SplitUserName( fullUserName, result.username, claimedAuthority );
00418             if ( claimedAuthority != result.authority )
00419             {
00420                 result.error << tOutput( "$login_error_unexpected_answer", 
00421                                          tString("PASSWORD_OK ") + result.username + "@" + result.authority,
00422                                          buf );
00423                 return;
00424             }
00425         }
00426 
00427         // read additional data, let caller handle it
00428         while( true )
00429         {
00430             tString blurb;
00431             blurb.ReadLine( content );
00432             if ( content.eof() || content.fail() )
00433             {
00434                 break;
00435             }
00436             result.blurb.push_back( blurb );
00437         }
00438 
00439         result.accessLevel = tAccessLevel_Remote;
00440         result.success = true;
00441         return;
00442     }
00443 }
00444 
00445 int nKrawall::FetchURL( tString const & authority, char const * query, std::ostream & target, int maxlen )
00446 {
00447     // compose real URL
00448     std::ostringstream fullURL;
00449     fullURL << "http://" << authority << "/armaauth/0.1";
00450     fullURL << query;
00451 
00452     // better not. output is not thread safe.
00453     // con << "Fetching authentication URL " << fullURL.str() << "\n";
00454 
00455     // fetch URL
00456     void * ctxt = xmlNanoHTTPOpen( fullURL.str().c_str(), NULL);
00457     if (ctxt == NULL)
00458     {
00459         return -1;
00460     }
00461 
00462     int rc = xmlNanoHTTPReturnCode(ctxt);
00463 
00464     // read content
00465     char buf[1000];
00466     buf[0] = 0;
00467     unsigned int len = 1;
00468     while ( len > 0 && maxlen > 0 )
00469     {
00470         int max = sizeof(buf);
00471         if ( max > maxlen )
00472             max = maxlen;
00473         len = xmlNanoHTTPRead( ctxt, &buf, max );
00474         target.write( buf, len );
00475         maxlen -= len;
00476     }
00477 
00478     xmlNanoHTTPClose(ctxt);
00479 
00480     return rc;
00481 }
00482 
00483 #ifdef KRAWALL_SERVER_LEAGUE
00484 // TODO: REALLY change this!!
00485 static nKrawall::nScrambledPassword key =
00486     {
00487         13, 12, 12, 12, 12, 12, 12, 12
00488     };
00489 
00490 
00491 // secret key to encrypt server->master server league transfer
00492 const nKrawall::nScrambledPassword& nKrawall::SecretLeagueKey()
00493 {
00494     return key;
00495 }
00496 
00497 // called ON THE MASTER when victim drives against killer's wall
00498 void nKrawall::MasterFrag(const tString &killer, const tString& victim)
00499 {
00500     con << killer << " killed " << victim << "\n";
00501     // TODO: REAL league management
00502 }
00503 
00504 
00505 // called ON THE MASTER at the end of a round; the last survivor is stored in
00506 // players[numPlayers-1], the first death in players[0]
00507 void nKrawall::MasterRoundEnd(const tString* players, int numPlayers)
00508 {
00509     if (numPlayers > 1)
00510     {
00511         con << players[numPlayers-1] << " survived over ";
00512         for (int i = numPlayers-2; i>=0; i--)
00513         {
00514             con << players[i];
00515             if (i > 0)
00516                 con << " and ";
00517         }
00518         con << ".\n";
00519     }
00520     // TODO: REAL league management
00521 }
00522 
00523 
00524 
00525 // first validity check for the league messages
00526 bool nKrawall::IsFromKrawall(tString& adress, unsigned int port)
00527 {
00528     return (adress.Len() > 3 &&
00529             !strncmp(adress, "127.0.0", 7));
00530 }
00531 
00532 // check if a user is from germany (so the master server will require
00533 // a password check)
00534 bool nKrawall::RequireMasterLogin(tString& adress, unsigned int port)
00535 {
00536     return (adress.Len() > 3 &&
00537             !strncmp(adress, "127.0.0", 7));
00538 }
00539 
00540 #endif
00541 #endif

Generated on Sat Mar 15 22:55:49 2008 for Armagetron Advanced by  doxygen 1.5.4