src/network/nAuthentication.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 */
00027 
00028 #include "nAuthentication.h"
00029 #include "tMemManager.h"
00030 #include "tToDo.h"
00031 #include "tLocale.h"
00032 #include "tRecorder.h"
00033 #include "tSysTime.h"
00034 
00035 #include "nNetwork.h"
00036 #include "nNetObject.h"
00037 #include "nSocket.h"
00038 
00039 #include <memory>
00040 #include <string>
00041 #include <string.h>
00042 
00043 #include <deque>
00044 
00045 #ifdef HAVE_LIBZTHREAD
00046 #include <zthread/Thread.h>
00047 #include <zthread/LockedQueue.h>
00048 //#include <zthread/ClassLockable.h>
00049 #include <zthread/FastMutex.h>
00050 #include <zthread/FastRecursiveMutex.h>
00051 #include <zthread/Guard.h>
00052 #include <zthread/SynchronousExecutor.h>
00053 #include <zthread/ThreadedExecutor.h>
00054 typedef ZThread::ThreadedExecutor nExecutor;
00055 //typedef ZThread::SynchronousExecutor nExecutor;
00056 typedef ZThread::FastMutex nMutex;
00057 #else
00058 typedef tNonMutex nMutex;
00059 #endif
00060 
00061 // authority black and whitelists
00062 static tString sn_AuthorityBlacklist, sn_AuthorityWhitelist;
00063 tConfItemLine  sn_AuthorityBlacklistConf( "AUTHORITY_BLACKLIST", sn_AuthorityBlacklist );
00064 tConfItemLine  sn_AuthorityWhitelistConf( "AUTHORITY_WHITELIST", sn_AuthorityWhitelist );
00065 
00066 #ifdef DEBUG
00067 // list of authorities that get accepted as valid authorities, no questions asked
00068 static tString sn_AuthorityNoCheck;
00069 tConfItemLine  sn_AuthorityNoCheckConf( "AUTHORITY_NO_CHECK", sn_AuthorityNoCheck );
00070 #endif
00071 
00072 static nAuthentication::UserPasswordCallback* S_UserPasswordCallback = NULL;
00073 static nAuthentication::LoginResultCallback*  S_LoginResultCallback  = NULL;
00074 
00075 // let the game register the callbacks
00076 void nAuthentication::SetUserPasswordCallback(nAuthentication::UserPasswordCallback* callback)
00077 {
00078     S_UserPasswordCallback = callback;
00079 }
00080 
00081 void nAuthentication::SetLoginResultCallback (nAuthentication::LoginResultCallback* callback)
00082 {
00083     S_LoginResultCallback = callback;
00084 }
00085 
00086 // network handler declarations
00087 
00088 static nDescriptor nPasswordRequest(40, &nAuthentication::HandlePasswordRequest, "password_request");
00089 
00090 static nDescriptor nPasswordAnswer(41, &nAuthentication::HandlePasswordAnswer, "password_answer");
00091 
00092 // password request and answer
00093 static nKrawall::nPasswordRequest sn_request;
00094 static nKrawall::nPasswordAnswer sn_answer;
00095 static nKrawall::nSalt sn_salt;
00096 static int s_inUse = false;
00097 
00098 // finish the request for username and password
00099 static void FinishHandlePasswordRequest()
00100 {
00101     nKrawall::nScrambledPassword egg;
00102 
00103     // if the callback exists, get the scrambled password of the wanted user
00104     if (S_UserPasswordCallback)
00105         (*S_UserPasswordCallback)( sn_request, sn_answer );
00106 
00107     // scramble the salt with the server address
00108     sn_GetAdr( 0, sn_answer.serverAddress );
00109     sn_request.ScrambleSalt( sn_salt, sn_answer.serverAddress );
00110 
00111     // scramble it with the given salt
00112     sn_request.ScrambleWithSalt( nKrawall::nScrambleInfo(sn_answer.username), sn_answer.scrambled, sn_salt, egg);
00113 
00114     // destroy the original password
00115     sn_answer.scrambled.Clear();
00116 
00117     // and send it back
00118     nMessage *ret = tNEW(nMessage)(nPasswordAnswer);
00119     nKrawall::WriteScrambledPassword(egg, *ret);
00120     *ret << sn_answer.username;
00121     *ret << sn_answer.aborted;
00122     *ret << sn_answer.automatic;
00123     *ret << sn_answer.serverAddress;
00124     ret->Send(0);
00125 
00126     s_inUse = false;
00127 }
00128 
00129 // receive a password request
00130 void nAuthentication::HandlePasswordRequest(nMessage& m)
00131 {
00132     if (m.SenderID() > 0 || sn_GetNetState() != nCLIENT)
00133         Cheater(m.SenderID());
00134 
00135     sn_answer = nKrawall::nPasswordAnswer();
00136     sn_request = nKrawall::nPasswordRequest();
00137 
00138     // already in the process: return without answer
00139     if ( s_inUse )
00140         return;
00141     s_inUse = true;
00142 
00143     // read salt and username from the message
00144     ReadSalt(m, sn_salt);
00145 
00146     // read the username as raw as sanely possible
00147     m.ReadRaw(sn_answer.username);
00148     sn_answer.username.NetFilter();
00149 
00150     m >> sn_request.message;
00151     if (!m.End())
00152     {
00153         m >> sn_request.failureOnLastTry;
00154     }
00155     else
00156     {
00157         sn_request.failureOnLastTry = true;
00158     }
00159     if (!m.End())
00160     {
00161         // read method, prefix and suffiox
00162         m >> sn_request.method;
00163         m.ReadRaw(sn_request.prefix);
00164         m.ReadRaw(sn_request.suffix);
00165         sn_request.prefix.NetFilter();
00166         sn_request.suffix.NetFilter();
00167     }
00168     else
00169     {
00170         // clear them
00171         sn_request.method = "bmd5";
00172         sn_request.prefix = "";
00173         sn_request.suffix = "";
00174     }
00175 
00176     // postpone the answer for a better opportunity since it
00177     // most likely involves opening a menu and waiting a while (and we
00178     // are right now in the process of fetching network messages...)
00179     st_ToDo(&FinishHandlePasswordRequest);
00180 }
00181 
00182 #ifdef KRAWALL_SERVER
00183 
00184 static int sn_UserID( nNetObject * o )
00185 {
00186     if ( !o )
00187     {
00188         return -1;
00189     }
00190     return o->Owner();
00191 }
00192 
00193 class nLoginProcess;
00194 
00196 class nLoginPersistence: 
00197     public nMachineDecorator
00198 {
00199     friend class nLoginProcess;
00200 
00201     nLoginPersistence( int userID )
00202     : nMachineDecorator( nMachine::GetMachine( userID ) ),
00203       userAuthFailedLastTime( false )
00204     {
00205     }
00206 
00207     static nLoginPersistence & Find( int userID )
00208     {
00209         nMachine & machine = nMachine::GetMachine( userID );
00210         nLoginPersistence * ret = machine.GetDecorator< nLoginPersistence >();
00211         if ( !ret )
00212         {
00213             ret = new nLoginPersistence( userID );
00214         }
00215 
00216         return *ret;
00217     }
00218     
00219     virtual void OnDestroy()
00220     {
00221         delete this;
00222     }
00223 
00224     bool userAuthFailedLastTime;
00225 };
00226 
00228 template< class T > class nMemberFunctionRunnerTemplate
00229 #ifdef HAVE_LIBZTHREAD
00230     : public ZThread::Runnable
00231 #endif
00232 {
00233 public:
00234     nMemberFunctionRunnerTemplate( T & object, void (T::*function)() )
00235     : object_( &object ), function_( function )
00236     {
00237     }
00238 
00239     // runs the function
00240     void run()
00241     {
00242         (object_->*function_)();
00243     }
00244 
00246     static void ScheduleBreak( T & object, void (T::*function)()  )
00247     {
00248         pendingForBreak_.push_back( nMemberFunctionRunnerTemplate( object, function ) );
00249     }
00250 
00252     static void ScheduleBackground( T & object, void (T::*function)()  )
00253     {
00254 #ifdef HAVE_LIBZTHREAD
00255         // schedule the task into a background thread
00256         static nExecutor executor;
00257         if ( !tRecorder::IsRunning() )
00258         {
00259             executor.execute( ZThread::Task( new nMemberFunctionRunnerTemplate( object, function ) ) );
00260         }
00261         else
00262         {
00263             // don't start threads when we're recording, just do the task at the next opportunity
00264             ScheduleBreak( object, function );
00265 
00266         }
00267 #else
00268         // do it when you can without getting interrupted.
00269         ScheduleBreak( object, function );
00270 #endif
00271     }
00272 
00274     static void ScheduleForeground( T & object, void (T::*function)()  )
00275     {
00276 #ifdef HAVE_LIBZTHREAD
00277         Pending().add( nMemberFunctionRunnerTemplate( object, function ) );
00278         st_ToDo( FinishAll );
00279 #else
00280         // execute it immedeately
00281         (object.*function)();
00282 #endif
00283 
00284     }
00285 
00286     // function that calls tasks scheduled for the next break
00287     static void OnBreak()
00288     {
00289         // finish all pending tasks
00290         while( pendingForBreak_.size() > 0 )
00291         {
00292             nMemberFunctionRunnerTemplate & next = pendingForBreak_.front();
00293             next.run();
00294             pendingForBreak_.pop_front();
00295         }
00296     }
00297 private:
00299     tJUST_CONTROLLED_PTR< T > object_;
00300     
00302     void (T::*function_)();
00303 
00304     // taks for the break
00305     static std::deque< nMemberFunctionRunnerTemplate > pendingForBreak_;
00306 
00307 #ifdef HAVE_LIBZTHREAD
00308     // queue of foreground tasks
00309      static ZThread::LockedQueue< nMemberFunctionRunnerTemplate, ZThread::FastMutex > & Pending()
00310      {
00311          static ZThread::LockedQueue< nMemberFunctionRunnerTemplate, ZThread::FastMutex > pending;
00312          return pending;
00313      }
00314     
00315     // function that calls them
00316     static void FinishAll()
00317     {
00318         // finish all pending tasks
00319         while( Pending().size() > 0 )
00320         {
00321             nMemberFunctionRunnerTemplate next = Pending().next();
00322             next.run();
00323         }
00324     }
00325 #endif
00326 };
00327 
00328 template< class T >
00329 std::deque< nMemberFunctionRunnerTemplate<T> > 
00330 nMemberFunctionRunnerTemplate<T>::pendingForBreak_;
00331 
00332 // convenience wrapper
00333 class nMemberFunctionRunner
00334 {
00335 public:
00336     enum ScheduleType
00337     {
00338         Break,
00339         Foreground,
00340         Background
00341     };
00342 
00343     template< class T > static void ScheduleBreak( T & object, void (T::*function)() )
00344     {
00345         nMemberFunctionRunnerTemplate<T>::ScheduleBreak( object, function );
00346     }
00347 
00348     template< class T > static void ScheduleBackground( T & object, void (T::*function)() )
00349     {
00350         nMemberFunctionRunnerTemplate<T>::ScheduleBackground( object, function );
00351     }
00352 
00353     template< class T > static void ScheduleForeground( T & object, void (T::*function)() )
00354     {
00355         nMemberFunctionRunnerTemplate<T>::ScheduleForeground( object, function );
00356     }
00357 
00358     template< class T > static void ScheduleMayBlock( T & object, void (T::*function)(), bool block )
00359     {
00360         if ( block )
00361         {
00362 #ifdef HAVE_LIBZTHREAD
00363             ScheduleBackground( object, function );
00364 #else
00365             ScheduleBreak( object, function );
00366 #endif
00367         }
00368         else
00369         {
00370             ScheduleForeground( object, function );
00371         }
00372     }
00373 };
00374 
00375 
00377 class nLoginProcess: 
00378     public nMachineDecorator, 
00379     public nKrawall::nCheckResult,
00380     public nKrawall::nPasswordCheckData,
00381     public tReferencable< nLoginProcess, nMutex >
00382 {
00383     // reference counting pointer 
00384     typedef tJUST_CONTROLLED_PTR< nLoginProcess > SelfPointer;
00385 public:
00386     nLoginProcess( int userID )
00387     : nMachineDecorator( nMachine::GetMachine( userID ) )
00388     {
00389         // install self reference to keep this object alive
00390         selfReference_ = this;
00391 
00392     // inform the user about delays
00393         bool delays = false;
00394 #ifdef HAVE_LIBZTHREAD
00395         delays = tRecorder::IsRunning();
00396 #endif
00397         if ( delays )
00398         {
00399             sn_ConsoleOut( tOutput( "$login_message_delayed" ), userID );
00400         }
00401     }
00402 
00403     ~nLoginProcess()
00404     {
00405     }
00406 
00407     static nLoginProcess * Find( int userID )
00408     {
00409         nMachine & machine = nMachine::GetMachine( userID );
00410         return machine.GetDecorator< nLoginProcess >();
00411     }
00412 
00413     // OK, authentication goes in several steps. First, we initialize everything
00414     void Init( tString const & authority, tString const & username, nNetObject & user, tString const & message )
00415     {
00416         this->user = &user;
00417         this->username = username;
00418         this->message = message;
00419         this->authority = authority;
00420 
00421         clientSupportedMethods = sn_Connections[user.Owner()].supportedAuthenticationMethods_;
00422 
00423         nMemberFunctionRunner::ScheduleMayBlock( *this, &nLoginProcess::FetchInfoFromAuthority, authority != "" );
00424     }
00425 
00426     // That function triggers fetching of authentication relevant data from the authentication
00427     // server in this function which is supposed to run in the background:
00428     void FetchInfoFromAuthority();
00429 
00430     // report an authority info query error to the higher level system
00431     bool ReportAuthorityError( tOutput const & error )
00432     {
00433         // prepare failure report
00434         this->success = false;
00435         this->error = error;
00436         
00437         Abort();
00438         
00439         return false;
00440     }
00441 
00442     // that function again triggers the following foreground action that queries 
00443     // the credentials from the client. This object then goes to sleep, 
00444     // waiting for a client answer or logout, whichever comes first.
00445     void QueryFromClient();
00446 
00447     // authentication data received from the client is processed here:
00448     void ProcessClientAnswer( nMessage & answer );
00449 
00450     // sanity check the server address
00451     bool CheckServerAddress( nMessage & m );
00452 
00453     // and here we go again: a background task talks with the authority
00454     // and determines whether the client is authorized or not.
00455     void Authorize();
00456 
00457     // which, when finished, triggers the foreground task of updating the
00458     // game state and informing the client of the success of the operation.
00459     void Finish();
00460 
00461     // the finish task can also be triggered any time by this function:
00462     void Abort();
00463 private:
00464     // helper functions
00465     
00466     // fetches info from remote authority
00467     bool FetchInfoFromAuthorityRemote();
00468 
00469     // fetches info from local authority
00470     bool FetchInfoFromAuthorityLocal();
00471 
00472     tString                message;     
00473 
00474 
00475     tString clientSupportedMethods;     
00476 
00477 
00478     // called when the machine gets destroyed, which happens a bit after
00479     // the client logged out. If no process is currently running, destroy the object.
00480     virtual void OnDestroy()
00481     {
00482         SelfPointer keepAlive( this );
00483         selfReference_ = 0;
00484     }
00485 
00487     SelfPointer selfReference_;
00488 };
00489 
00490 
00491 
00492 // That function triggers fetching of authentication relevant data from the authentication
00493 // server in this function which is supposed to run in the background:
00494 void nLoginProcess::FetchInfoFromAuthority()
00495 {
00496     // set method to defaults
00497     method.method = "bmd5";
00498     method.prefix = "";
00499     method.suffix = "";
00500     
00501     bool ret;
00502     if ( !tRecorder::IsPlayingBack() )
00503     {
00504         if ( authority.Len() <= 1 )
00505         {
00506             // local logins are easy, handle them first
00507             ret = FetchInfoFromAuthorityLocal();
00508         }
00509         else
00510         {
00511             // remote logins are harder.
00512             ret = FetchInfoFromAuthorityRemote();
00513         }
00514     }
00515 
00516     // record and playback result.
00517     static char const * section = "AUTH_INFO";
00518     tRecorder::Playback( section, ret );
00519     tRecorder::Playback( section, method.method );
00520     tRecorder::Playback( section, method.prefix );
00521     tRecorder::Playback( section, method.suffix );
00522     tRecorder::Playback( section, authority );
00523     tRecorder::Playback( section, error );
00524     tRecorder::Record( section, ret );
00525     tRecorder::Record( section, method.method );
00526     tRecorder::Record( section, method.prefix );
00527     tRecorder::Record( section, method.suffix );
00528     tRecorder::Record( section, authority );
00529     tRecorder::Record( section, error );
00530 
00531     if ( !ret )
00532     {
00533         if ( tRecorder::IsPlayingBack() )
00534         {
00535             Abort();
00536         }
00537 
00538         return;
00539     }
00540 
00541     // and go on
00542     nMemberFunctionRunner::ScheduleForeground( *this, &nLoginProcess::QueryFromClient );
00543 }
00544 
00545 static bool sn_supportRemoteLogins = false;
00546 static tSettingItem< bool > sn_supportRemoteLoginsConf( "GLOBAL_ID", sn_supportRemoteLogins );
00547 
00548 // legal characters in authority hostnames(besides alnum and dots)
00549 static bool sn_IsLegalSpecialChar( char c )
00550 {
00551     switch (c)
00552     {
00553     case '-': // well, ok, this character actually happens to be in many URLs :)
00554     case '+': // these not, but let's consider them legal.
00555     case '=':
00556     case '_':
00557         return true;
00558     default:
00559         return false;
00560     }
00561 }
00562 
00563 // fetches info from remote authority
00564 bool nLoginProcess::FetchInfoFromAuthorityRemote()
00565 {
00566     if ( !sn_supportRemoteLogins )
00567     {
00568         return ReportAuthorityError( tOutput("$login_error_noremote") );
00569     }
00570 
00571     {
00572         // the hostname part of the authority should not contain uppercase letters
00573 #ifdef DEBUG
00574         if ( tIsInList( sn_AuthorityNoCheck, authority ) )
00575         {
00576             fullAuthority = authority;
00577         }
00578         else
00579 #endif
00580         {
00581             std::istringstream in( static_cast< const char * >( authority ) );
00582             std::ostringstream outShort; // stream for shorthand authority
00583             std::ostringstream outFull;  // stream for full authority URL that is to be used for lookups     
00584             int c = in.get();
00585 
00586             // is the authority an abreviation?
00587             bool shortcut = true;
00588 
00589             // is the server a raw IP?
00590             bool rawIP = true;
00591 
00592             // which part we're currently parsing
00593             bool inHostName = true;
00594             bool inPort = false;
00595             bool slash = false;
00596             int port = 0;
00597 
00598             while( !in.eof() )
00599             {
00600                 if ( inHostName )
00601                 {
00602                     // check validity of hostname part
00603                     if ( c == '.' )
00604                     {
00605                         shortcut = false;
00606                     }
00607                     else if ( isalnum(c) )
00608                     {
00609                         c = tolower(c);
00610                         if ( !isdigit( c ) )
00611                         {
00612                             rawIP = false;
00613                         }
00614                     }
00615                     else if ( c == ':' )
00616                     {
00617                         inPort = true;
00618                         inHostName = false;
00619                     }
00620                     else if ( c == '/' )
00621                     {
00622                         shortcut = false;
00623                         slash = true;
00624                         inHostName = false;
00625                     }
00626                     else if ( !sn_IsLegalSpecialChar(c) )
00627                     {
00628                         return ReportAuthorityError( tOutput( "$login_error_invalidurl_illegal_hostname", authority ) );
00629                     }
00630                 }
00631                 else if ( inPort )
00632                 {
00633                     if ( c == '/' )
00634                     {
00635                         shortcut = false;
00636                         inPort = false;
00637                         slash = true;
00638                     }
00639                     else if ( !isdigit( c ) )
00640                     {
00641                         return ReportAuthorityError( tOutput( "$login_error_invalidurl_illegal_port", authority ) );
00642                     }
00643                     else
00644                     {
00645                         port *= 10;
00646                         port += c - '0';
00647                     }
00648                 }
00649                 else // must be in path
00650                 {
00651                     if ( c == '/' )
00652                     {
00653                         if ( slash )
00654                         {
00655                             return ReportAuthorityError( tOutput( "$login_error_invalidurl_slash", authority ) );
00656                         }
00657 
00658                         slash = true;
00659                     }
00660                     else
00661                     {
00662                         if (!isalnum(c) && c != '.' && c != '~' && !sn_IsLegalSpecialChar(c) )
00663                         {
00664                             return ReportAuthorityError( tOutput( "$login_error_invalidurl_illegal_path", authority )  );
00665                         }
00666 
00667                         slash = false;
00668                     }
00669                 }
00670 
00671                 // shorthand authority must consist of lowercase letters only
00672                 outShort.put(tolower(c));
00673 
00674                 outFull.put(c);
00675 
00676                 c = in.get();
00677             }
00678             if ( slash )
00679             {
00680                 return ReportAuthorityError( tOutput( "$login_error_invalidurl_slash", authority ) );
00681             }
00682             if ( port == 80 )
00683             {
00684                 return ReportAuthorityError( tOutput( "$login_error_invalidurl_defaultport", authority ) );
00685             }
00686 
00687             if ( rawIP )
00688             {
00689                 return ReportAuthorityError( tOutput( "$login_error_invalidurl_rawip", authority ) );
00690             }
00691 
00692             authority = outShort.str().c_str();
00693             fullAuthority = outFull.str().c_str();
00694 
00695             static const char * def = ".authentication.armagetronad.net";
00696 
00697             // append default authority path
00698             if ( authority.Len() > 1 && shortcut )
00699             {
00700                 fullAuthority += def;
00701             }
00702 
00703             // check if the pased authority contains the default ending
00704             if ( !shortcut && authority.Reverse().StartsWith( tString( def ).Reverse() ) )
00705             {
00706                 // strip it
00707                 authority = authority.SubStr( 0, authority.Len() - strlen( def ) - 1 );
00708                 shortcut = true;
00709             }
00710         }
00711 
00712         // check for authority in black and whitelist
00713         if ( tIsInList( sn_AuthorityBlacklist, authority ) )
00714         {
00715             return ReportAuthorityError( tOutput( "$login_error_blacklist", authority ) );
00716         }
00717 
00718         if ( sn_AuthorityWhitelist != "" && !tIsInList( sn_AuthorityWhitelist, authority ) )
00719         {
00720             return ReportAuthorityError( tOutput( "$login_error_whitelist", authority ) );
00721         }
00722 
00723         // try yo find a better method, fetch method list
00724         std::stringstream answer;
00725         int rc = nKrawall::FetchURL( fullAuthority, "?query=methods", answer );
00726 
00727         if ( rc == -1 )
00728         {
00729             return ReportAuthorityError( tOutput( "$login_error_invalidurl_notfound", authority ) );
00730         }
00731          
00732         tString id;
00733         answer >> id;
00734         tToLower(id);
00735 
00736         tString methods;
00737         std::ws(answer);
00738         methods.ReadLine( answer );
00739         tToLower(methods);
00740 
00741         if ( rc != 200 || id != "methods" )
00742         {
00743             return ReportAuthorityError( tOutput( "$login_error_nomethodlist", authority, rc, id + " " + methods ) );
00744         }
00745        
00746 
00747         method.method = nKrawall::nMethod::BestMethod( 
00748             methods,
00749             clientSupportedMethods
00750             );
00751         
00752         // check whether a method can be found
00753         if ( method.method.Len() <= 1 )
00754         {
00755             return ReportAuthorityError(
00756                 tOutput( "$login_error_nomethod", 
00757                          clientSupportedMethods,
00758                          nKrawall::nMethod::SupportedMethods(),
00759                          methods )
00760                 );
00761         }
00762     }
00763         
00764     // fetch md5 prefix and suffix
00765     {
00766         std::ostringstream query;
00767         query << "?query=params";
00768         query << "&method=" << nKrawall::EncodeString( method.method );
00769         std::ostringstream data;
00770         int rc = nKrawall::FetchURL( fullAuthority, query.str().c_str(), data );
00771         
00772         if ( rc != 200 )
00773         {
00774             if ( rc == -1 )
00775             {
00776                 return ReportAuthorityError( tOutput( "$login_error_invalidurl_notfound", authority ) );
00777             }
00778             
00779             return ReportAuthorityError( tOutput( "$login_error_nomethodproperties", authority, rc, data.str().c_str() ) );
00780         }
00781         
00782         // read the properties
00783         std::istringstream read( data.str() );
00784         method = nKrawall::nMethod( static_cast< char const * >( method.method ), read );
00785     }
00786     
00787     return true;
00788 }
00789 
00790 // fetches info from local authority
00791 bool nLoginProcess::FetchInfoFromAuthorityLocal()
00792 {
00793     // try yo find a better method
00794     if ( !nKrawall::nMethod::BestLocalMethod( 
00795              clientSupportedMethods,
00796              method
00797              )
00798         )
00799     {
00800         return ReportAuthorityError(
00801             tOutput( "$login_error_nomethod", 
00802                      clientSupportedMethods,
00803                      nKrawall::nMethod::SupportedMethods(),
00804                      nKrawall::nMethod::SupportedMethods() )
00805             );
00806     }
00807     
00808     return true;
00809 }
00810 
00811 // that function again triggers the following foreground action that queries 
00812 // the credentials from the client. This object then goes to sleep, 
00813 // waiting for a client answer or logout, whichever comes first.
00814 void nLoginProcess::QueryFromClient()
00815 {
00816     // check whether the user disappeared by now (this is run in the main thread,
00817     // so no risk of the user disconnecting while the function runs)
00818     int userID = sn_UserID( user );
00819     if ( userID <= 0 )
00820         return;
00821 
00822     // create a random salt value
00823     nKrawall::RandomSalt(salt);
00824     
00825     // send the salt value and the username to the
00826     nMessage *m = tNEW(nMessage)(::nPasswordRequest);
00827     nKrawall::WriteSalt(salt, *m);
00828     *m << username;
00829     *m << static_cast<tString>(message);
00830     *m << nLoginPersistence::Find( userID ).userAuthFailedLastTime;
00831     
00832     // write method info
00833     *m << method.method;
00834     *m << method.prefix;
00835     *m << method.suffix;
00836     
00837     m->Send(userID);
00838 
00839     // well, then we wait for the answer.
00840     con << tOutput( "$login_message_responded", userID, username, method.method, message );
00841 }
00842 
00843 // authentication data received from the client is processed here:
00844 void nLoginProcess::ProcessClientAnswer( nMessage & m )
00845 {
00846     success = false;
00847     
00848     // read password and username from remote
00849     nKrawall::ReadScrambledPassword(m, hash);
00850 
00851     m.ReadRaw(username);
00852     username.NetFilter();
00853 
00854     aborted = false;
00855     automatic = false;
00856     if ( !m.End() )
00857     {
00858         m >> aborted;
00859     }
00860     if ( !m.End() )
00861     {
00862         m >> automatic;
00863     }
00864     if (!m.End())
00865     {
00866         // read the server address the client used for scrambling
00867         m >> serverAddress;
00868 
00869         // sanity check it, of course :)
00870         if ( !CheckServerAddress( m ) )
00871         {
00872             // no use going on, the server address won't match, password checking will fail.
00873             return;
00874         }
00875     }
00876     else
00877     {
00878         serverAddress = sn_GetMyAddress();
00879 
00880         if ( method.method != "bmd5" )
00881         {
00882             con << "WARNING, client did not send the server address. Password checks may fail.\n";
00883         }
00884     }
00885   
00886     // and go on
00887     nMemberFunctionRunner::ScheduleMayBlock( *this, &nLoginProcess::Authorize, authority != "" );
00888 }
00889 
00890 static bool sn_trustLAN = false;
00891 static tSettingItem< bool > sn_TrustLANConf( "TRUST_LAN", sn_trustLAN );
00892 
00893 // sanity check the server address
00894 bool nLoginProcess::CheckServerAddress( nMessage & m )
00895 {
00896     // check whether we can read our IP from the socket
00897     nSocket const * socket = sn_Connections[m.SenderID()].socket;
00898     if ( socket )
00899     {
00900         tString compareAddress = socket->GetAddress().ToString();
00901         if ( !compareAddress.StartsWith("*") && compareAddress == serverAddress )
00902         {
00903             // everything is fine, adresses match
00904             return true;
00905         }
00906     }
00907 
00908     // check the incoming address, clients from the LAN should be safe
00909     if ( sn_trustLAN )
00910     {
00911         tString peerAddress;
00912         sn_GetAdr( m.SenderID(), peerAddress );
00913         if ( sn_IsLANAddress( peerAddress ) && sn_IsLANAddress( serverAddress ) )
00914         {
00915             return true;
00916         }
00917     }
00918 
00919     else if ( sn_GetMyAddress() == serverAddress )
00920     {
00921         // all's well
00922         return true;
00923     }
00924 
00925     tString hisServerAddress = serverAddress;
00926     serverAddress = sn_GetMyAddress();
00927 
00928     // if we don't know our own address, 
00929     if ( sn_GetMyAddress().StartsWith("*") )
00930     {
00931         // reject authentication.
00932         return ReportAuthorityError( tOutput("$login_error_pharm", hisServerAddress, sn_GetMyAddress() ) );
00933     }
00934 
00935     // reject authentication.
00936     return ReportAuthorityError( tOutput("$login_error_pharm", hisServerAddress, sn_GetMyAddress() ) );
00937 }
00938 
00939 // and here we go again: a background task talks with the authority
00940 // and determines whether the client is authorized or not.
00941 void nLoginProcess::Authorize()
00942 {
00943     if ( aborted )
00944     {
00945         success = false;
00946         
00947         error = tOutput("$login_error_aborted");
00948     }
00949     else
00950     {
00951         if ( !tRecorder::IsPlayingBack() )
00952         {
00953             nKrawall::CheckScrambledPassword( *this, *this );
00954         }
00955 
00956         // record and playback result (required because on playback, a new
00957         // salt is generated and this way, a recoding does not contain ANY
00958         // exploitable information for password theft: the scrambled password
00959         // stored in the incoming network stream has an unknown salt value. )
00960         static char const * section = "AUTH_RESULT";
00961         tRecorder::Playback( section, username );
00962         tRecorder::Playback( section, success );
00963         tRecorder::Playback( section, authority );
00964         tRecorder::Playback( section, error );
00965         tRecorder::Record( section, username );
00966         tRecorder::Record( section, success );
00967         tRecorder::Record( section, authority );
00968         tRecorder::Record( section, error );
00969     }
00970     
00971     Abort();
00972 }
00973 
00974 // the finish task can also be triggered any time by this function:
00975 void nLoginProcess::Abort()
00976 {
00977     nMemberFunctionRunner::ScheduleForeground( *this, &nLoginProcess::Finish );
00978 }
00979 
00980 // which, when finished, triggers the foreground task of updating the
00981 // game state and informing the client of the success of the operation.
00982 void nLoginProcess::Finish()
00983 {
00984     // again, userID is safe in this function
00985     int userID = sn_UserID( user );
00986     if ( userID <= 0 )
00987         return;
00988 
00989     // decorate console with correct sender ID
00990     nCurrentSenderID currentSender( userID );
00991     
00992     // store success for next time
00993     nLoginPersistence::Find( userID ).userAuthFailedLastTime = !success;
00994 
00995     // remove this decorator from public view
00996     Remove();
00997     
00998     if (S_LoginResultCallback)
00999         (*S_LoginResultCallback)( *this );
01000 
01001     // bye-bye!
01002     Destroy();
01003 }
01004 
01005 static void sn_Reset(){
01006     int userID = nCallbackLoginLogout::User();
01007 
01008     // kill/detach pending login process
01009     nLoginProcess * process = nLoginProcess::Find( userID );
01010     if ( process )
01011     {
01012         process->Remove();
01013         process->Destroy();
01014     }
01015 }
01016 
01017 static nCallbackLoginLogout reset(&sn_Reset);
01018 
01019 #endif // KRAWALL_SERVER
01020     
01021 void nAuthentication::HandlePasswordAnswer(nMessage& m)
01022 {
01023 #ifdef KRAWALL_SERVER
01024     // find login pricess
01025     nLoginProcess * process = nLoginProcess::Find( m.SenderID() );
01026 
01027     // and delegate to it
01028     if ( process )
01029     {
01030         process->ProcessClientAnswer( m );
01031     }
01032 #endif
01033 }
01034         
01035 // on the server: request user authentification from login slot
01036 bool nAuthentication::RequestLogin(const tString & authority, const tString& username, nNetObject & user, const tOutput& message )
01037 {
01038 #ifdef KRAWALL_SERVER
01039     int userID = user.Owner();
01040     if ( userID <= 0 )
01041     {
01042         return false;
01043     }
01044 
01045     con << tOutput( "$login_message_requested", userID, username, authority );
01046 
01047     // do nothing if there is another login in process for that client
01048     if ( nLoginProcess::Find( userID ) )
01049     {
01050         return false;
01051     }
01052 
01053     // trigger function cascade bouncing between threads
01054     (new nLoginProcess( userID ))->Init( authority, username, user, tString(message) );
01055 #endif
01056 
01057     return true;
01058 }
01059 
01061 void nAuthentication::OnBreak()
01062 {
01063 #ifdef KRAWALL_SERVER
01064     nMemberFunctionRunnerTemplate< nLoginProcess >::OnBreak();
01065 #endif
01066     st_DoToDo();
01067 }
01068 

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