3
*************************************************************************
5
ArmageTron -- Just another Tron Lightcycle Game in 3D.
6
Copyright (C) 2000 Manuel Moos (manuel@moosnet.de)
8
**************************************************************************
10
This program is free software; you can redistribute it and/or
11
modify it under the terms of the GNU General Public License
12
as published by the Free Software Foundation; either version 2
13
of the License, or (at your option) any later version.
15
This program is distributed in the hope that it will be useful,
16
but WITHOUT ANY WARRANTY; without even the implied warranty of
17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
GNU General Public License for more details.
20
You should have received a copy of the GNU General Public License
21
along with this program; if not, write to the Free Software
22
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
***************************************************************************
28
#include "nAuthentication.h"
29
#include "tMemManager.h"
32
#include "tRecorder.h"
36
#include "nNetObject.h"
44
#ifdef HAVE_LIBZTHREAD
45
#include <zthread/Thread.h>
46
#include <zthread/LockedQueue.h>
47
//#include <zthread/ClassLockable.h>
48
#include <zthread/FastMutex.h>
49
#include <zthread/FastRecursiveMutex.h>
50
#include <zthread/Guard.h>
51
#include <zthread/SynchronousExecutor.h>
52
#include <zthread/ThreadedExecutor.h>
53
typedef ZThread::ThreadedExecutor nExecutor;
54
//typedef ZThread::SynchronousExecutor nExecutor;
55
typedef ZThread::FastMutex nMutex;
57
typedef tNonMutex nMutex;
60
// authority black and whitelists
61
static tString sn_AuthorityBlacklist, sn_AuthorityWhitelist;
62
tConfItemLine sn_AuthorityBlacklistConf( "AUTHORITY_BLACKLIST", sn_AuthorityBlacklist );
63
tConfItemLine sn_AuthorityWhitelistConf( "AUTHORITY_WHITELIST", sn_AuthorityWhitelist );
66
// list of authorities that get accepted as valid authorities, no questions asked
67
static tString sn_AuthorityNoCheck;
68
tConfItemLine sn_AuthorityNoCheckConf( "AUTHORITY_NO_CHECK", sn_AuthorityNoCheck );
71
static nAuthentication::UserPasswordCallback* S_UserPasswordCallback = NULL;
72
static nAuthentication::LoginResultCallback* S_LoginResultCallback = NULL;
74
// let the game register the callbacks
75
void nAuthentication::SetUserPasswordCallback(nAuthentication::UserPasswordCallback* callback)
77
S_UserPasswordCallback = callback;
80
void nAuthentication::SetLoginResultCallback (nAuthentication::LoginResultCallback* callback)
82
S_LoginResultCallback = callback;
85
// network handler declarations
87
static nDescriptor nPasswordRequest(40, &nAuthentication::HandlePasswordRequest, "password_request");
89
static nDescriptor nPasswordAnswer(41, &nAuthentication::HandlePasswordAnswer, "password_answer");
91
// password request and answer
92
static nKrawall::nPasswordRequest sn_request;
93
static nKrawall::nPasswordAnswer sn_answer;
94
static nKrawall::nSalt sn_salt;
95
static int s_inUse = false;
97
// finish the request for username and password
98
static void FinishHandlePasswordRequest()
100
nKrawall::nScrambledPassword egg;
102
// if the callback exists, get the scrambled password of the wanted user
103
if (S_UserPasswordCallback)
104
(*S_UserPasswordCallback)( sn_request, sn_answer );
106
// scramble the salt with the server address
107
sn_GetAdr( 0, sn_answer.serverAddress );
108
sn_request.ScrambleSalt( sn_salt, sn_answer.serverAddress );
110
// scramble it with the given salt
111
sn_request.ScrambleWithSalt( nKrawall::nScrambleInfo(sn_answer.username), sn_answer.scrambled, sn_salt, egg);
113
// destroy the original password
114
sn_answer.scrambled.Clear();
117
nMessage *ret = tNEW(nMessage)(nPasswordAnswer);
118
nKrawall::WriteScrambledPassword(egg, *ret);
119
*ret << sn_answer.username;
120
*ret << sn_answer.aborted;
121
*ret << sn_answer.automatic;
122
*ret << sn_answer.serverAddress;
128
// receive a password request
129
void nAuthentication::HandlePasswordRequest(nMessage& m)
131
if (m.SenderID() > 0 || sn_GetNetState() != nCLIENT)
132
Cheater(m.SenderID());
134
sn_answer = nKrawall::nPasswordAnswer();
135
sn_request = nKrawall::nPasswordRequest();
137
// already in the process: return without answer
142
// read salt and username from the message
143
ReadSalt(m, sn_salt);
145
// read the username as raw as sanely possible
146
m.ReadRaw(sn_answer.username);
147
sn_answer.username.NetFilter();
149
m >> sn_request.message;
152
m >> sn_request.failureOnLastTry;
156
sn_request.failureOnLastTry = true;
160
// read method, prefix and suffiox
161
m >> sn_request.method;
162
m.ReadRaw(sn_request.prefix);
163
m.ReadRaw(sn_request.suffix);
164
sn_request.prefix.NetFilter();
165
sn_request.suffix.NetFilter();
170
sn_request.method = "bmd5";
171
sn_request.prefix = "";
172
sn_request.suffix = "";
175
// postpone the answer for a better opportunity since it
176
// most likely involves opening a menu and waiting a while (and we
177
// are right now in the process of fetching network messages...)
178
st_ToDo(&FinishHandlePasswordRequest);
181
#ifdef KRAWALL_SERVER
183
static int sn_UserID( nNetObject * o )
194
//! persistent information between login processes
195
class nLoginPersistence:
196
public nMachineDecorator
198
friend class nLoginProcess;
200
nLoginPersistence( int userID )
201
: nMachineDecorator( nMachine::GetMachine( userID ) ),
202
userAuthFailedLastTime( false )
206
static nLoginPersistence & Find( int userID )
208
nMachine & machine = nMachine::GetMachine( userID );
209
nLoginPersistence * ret = machine.GetDecorator< nLoginPersistence >();
212
ret = new nLoginPersistence( userID );
218
virtual void OnDestroy()
223
bool userAuthFailedLastTime;
226
//! template that runs void member functions of reference countable objects
227
template< class T > class nMemberFunctionRunnerTemplate
228
#ifdef HAVE_LIBZTHREAD
229
: public ZThread::Runnable
233
nMemberFunctionRunnerTemplate( T & object, void (T::*function)() )
234
: object_( &object ), function_( function )
241
(object_->*function_)();
244
//! schedule a task for execution at the next convenient break, between game rounds for example
245
static void ScheduleBreak( T & object, void (T::*function)() )
247
pendingForBreak_.push_back( nMemberFunctionRunnerTemplate( object, function ) );
250
//! schedule a task for execution in a background thread
251
static void ScheduleBackground( T & object, void (T::*function)() )
253
#ifdef HAVE_LIBZTHREAD
254
// schedule the task into a background thread
255
static nExecutor executor;
256
if ( !tRecorder::IsRunning() )
258
executor.execute( ZThread::Task( new nMemberFunctionRunnerTemplate( object, function ) ) );
262
// don't start threads when we're recording, just do the task at the next opportunity
263
ScheduleBreak( object, function );
267
// do it when you can without getting interrupted.
268
ScheduleBreak( object, function );
272
//! schedule a task for execution in the next tToDo call
273
static void ScheduleForeground( T & object, void (T::*function)() )
275
#ifdef HAVE_LIBZTHREAD
276
Pending().add( nMemberFunctionRunnerTemplate( object, function ) );
277
st_ToDo( FinishAll );
279
// execute it immedeately
280
(object.*function)();
285
// function that calls tasks scheduled for the next break
286
static void OnBreak()
288
// finish all pending tasks
289
while( pendingForBreak_.size() > 0 )
291
nMemberFunctionRunnerTemplate & next = pendingForBreak_.front();
293
pendingForBreak_.pop_front();
297
//! pointer to the object we should so something with
298
tJUST_CONTROLLED_PTR< T > object_;
300
//! the function to call
301
void (T::*function_)();
303
// taks for the break
304
static std::deque< nMemberFunctionRunnerTemplate > pendingForBreak_;
306
#ifdef HAVE_LIBZTHREAD
307
// queue of foreground tasks
308
static ZThread::LockedQueue< nMemberFunctionRunnerTemplate, ZThread::FastMutex > & Pending()
310
static ZThread::LockedQueue< nMemberFunctionRunnerTemplate, ZThread::FastMutex > pending;
314
// function that calls them
315
static void FinishAll()
317
// finish all pending tasks
318
while( Pending().size() > 0 )
320
nMemberFunctionRunnerTemplate next = Pending().next();
328
std::deque< nMemberFunctionRunnerTemplate<T> >
329
nMemberFunctionRunnerTemplate<T>::pendingForBreak_;
331
// convenience wrapper
332
class nMemberFunctionRunner
342
template< class T > static void ScheduleBreak( T & object, void (T::*function)() )
344
nMemberFunctionRunnerTemplate<T>::ScheduleBreak( object, function );
347
template< class T > static void ScheduleBackground( T & object, void (T::*function)() )
349
nMemberFunctionRunnerTemplate<T>::ScheduleBackground( object, function );
352
template< class T > static void ScheduleForeground( T & object, void (T::*function)() )
354
nMemberFunctionRunnerTemplate<T>::ScheduleForeground( object, function );
357
template< class T > static void ScheduleMayBlock( T & object, void (T::*function)(), bool block )
361
#ifdef HAVE_LIBZTHREAD
362
ScheduleBackground( object, function );
364
ScheduleBreak( object, function );
369
ScheduleForeground( object, function );
375
//! manager for logon processes
377
public nMachineDecorator,
378
public nKrawall::nCheckResult,
379
public nKrawall::nPasswordCheckData,
380
public tReferencable< nLoginProcess, nMutex >
382
// reference counting pointer
383
typedef tJUST_CONTROLLED_PTR< nLoginProcess > SelfPointer;
385
nLoginProcess( int userID )
386
: nMachineDecorator( nMachine::GetMachine( userID ) )
388
// install self reference to keep this object alive
389
selfReference_ = this;
391
// inform the user about delays
393
#ifdef HAVE_LIBZTHREAD
394
delays = tRecorder::IsRunning();
398
sn_ConsoleOut( tOutput( "$login_message_delayed" ), userID );
406
static nLoginProcess * Find( int userID )
408
nMachine & machine = nMachine::GetMachine( userID );
409
return machine.GetDecorator< nLoginProcess >();
412
// OK, authentication goes in several steps. First, we initialize everything
413
void Init( tString const & authority, tString const & username, nNetObject & user, tString const & message )
416
this->username = username;
417
this->message = message;
418
this->authority = authority;
420
clientSupportedMethods = sn_Connections[user.Owner()].supportedAuthenticationMethods_;
422
nMemberFunctionRunner::ScheduleMayBlock( *this, &nLoginProcess::FetchInfoFromAuthority, authority != "" );
425
// That function triggers fetching of authentication relevant data from the authentication
426
// server in this function which is supposed to run in the background:
427
void FetchInfoFromAuthority();
429
// report an authority info query error to the higher level system
430
bool ReportAuthorityError( tOutput const & error )
432
// prepare failure report
433
this->success = false;
441
// that function again triggers the following foreground action that queries
442
// the credentials from the client. This object then goes to sleep,
443
// waiting for a client answer or logout, whichever comes first.
444
void QueryFromClient();
446
// authentication data received from the client is processed here:
447
void ProcessClientAnswer( nMessage & answer );
449
// sanity check the server address
450
bool CheckServerAddress( nMessage & m );
452
// and here we go again: a background task talks with the authority
453
// and determines whether the client is authorized or not.
456
// which, when finished, triggers the foreground task of updating the
457
// game state and informing the client of the success of the operation.
460
// the finish task can also be triggered any time by this function:
465
// fetches info from remote authority
466
bool FetchInfoFromAuthorityRemote();
468
// fetches info from local authority
469
bool FetchInfoFromAuthorityLocal();
471
tString message; //!< message to present to user
474
tString clientSupportedMethods; //!< methods supported by the client
477
// called when the machine gets destroyed, which happens a bit after
478
// the client logged out. If no process is currently running, destroy the object.
479
virtual void OnDestroy()
481
SelfPointer keepAlive( this );
485
//! pointer to self to keep the object alive while the machine exists
486
SelfPointer selfReference_;
491
// That function triggers fetching of authentication relevant data from the authentication
492
// server in this function which is supposed to run in the background:
493
void nLoginProcess::FetchInfoFromAuthority()
495
// set method to defaults
496
method.method = "bmd5";
501
if ( !tRecorder::IsPlayingBack() )
503
if ( authority.Len() <= 1 )
505
// local logins are easy, handle them first
506
ret = FetchInfoFromAuthorityLocal();
510
// remote logins are harder.
511
ret = FetchInfoFromAuthorityRemote();
515
// record and playback result.
516
static char const * section = "AUTH_INFO";
517
tRecorder::Playback( section, ret );
518
tRecorder::Playback( section, method.method );
519
tRecorder::Playback( section, method.prefix );
520
tRecorder::Playback( section, method.suffix );
521
tRecorder::Playback( section, authority );
522
tRecorder::Playback( section, error );
523
tRecorder::Record( section, ret );
524
tRecorder::Record( section, method.method );
525
tRecorder::Record( section, method.prefix );
526
tRecorder::Record( section, method.suffix );
527
tRecorder::Record( section, authority );
528
tRecorder::Record( section, error );
532
if ( tRecorder::IsPlayingBack() )
541
nMemberFunctionRunner::ScheduleForeground( *this, &nLoginProcess::QueryFromClient );
544
static bool sn_supportRemoteLogins = false;
545
static tSettingItem< bool > sn_supportRemoteLoginsConf( "GLOBAL_ID", sn_supportRemoteLogins );
547
// legal characters in authority hostnames(besides alnum and dots)
548
static bool sn_IsLegalSpecialChar( char c )
552
case '-': // well, ok, this character actually happens to be in many URLs :)
553
case '+': // these not, but let's consider them legal.
562
// fetches info from remote authority
563
bool nLoginProcess::FetchInfoFromAuthorityRemote()
565
if ( !sn_supportRemoteLogins )
567
return ReportAuthorityError( tOutput("$login_error_noremote") );
571
// the hostname part of the authority should not contain uppercase letters
573
if ( tIsInList( sn_AuthorityNoCheck, authority ) )
575
fullAuthority = authority;
580
std::istringstream in( static_cast< const char * >( authority ) );
581
std::ostringstream outShort; // stream for shorthand authority
582
std::ostringstream outFull; // stream for full authority URL that is to be used for lookups
585
// is the authority an abreviation?
586
bool shortcut = true;
588
// is the server a raw IP?
591
// which part we're currently parsing
592
bool inHostName = true;
601
// check validity of hostname part
606
else if ( isalnum(c) )
625
else if ( !sn_IsLegalSpecialChar(c) )
627
return ReportAuthorityError( tOutput( "$login_error_invalidurl_illegal_hostname", authority ) );
638
else if ( !isdigit( c ) )
640
return ReportAuthorityError( tOutput( "$login_error_invalidurl_illegal_port", authority ) );
648
else // must be in path
654
return ReportAuthorityError( tOutput( "$login_error_invalidurl_slash", authority ) );
661
if (!isalnum(c) && c != '.' && c != '~' && !sn_IsLegalSpecialChar(c) )
663
return ReportAuthorityError( tOutput( "$login_error_invalidurl_illegal_path", authority ) );
670
// shorthand authority must consist of lowercase letters only
671
outShort.put(tolower(c));
679
return ReportAuthorityError( tOutput( "$login_error_invalidurl_slash", authority ) );
683
return ReportAuthorityError( tOutput( "$login_error_invalidurl_defaultport", authority ) );
688
return ReportAuthorityError( tOutput( "$login_error_invalidurl_rawip", authority ) );
691
authority = outShort.str().c_str();
692
fullAuthority = outFull.str().c_str();
694
static const char * def = ".authentication.armagetronad.net";
696
// append default authority path
697
if ( authority.Len() > 1 && shortcut )
699
fullAuthority += def;
702
// check if the pased authority contains the default ending
703
if ( !shortcut && authority.Reverse().StartsWith( tString( def ).Reverse() ) )
706
authority = authority.SubStr( 0, authority.Len() - strlen( def ) - 1 );
711
// check for authority in black and whitelist
712
if ( tIsInList( sn_AuthorityBlacklist, authority ) )
714
return ReportAuthorityError( tOutput( "$login_error_blacklist", authority ) );
717
if ( sn_AuthorityWhitelist != "" && !tIsInList( sn_AuthorityWhitelist, authority ) )
719
return ReportAuthorityError( tOutput( "$login_error_whitelist", authority ) );
722
// try yo find a better method, fetch method list
723
std::stringstream answer;
724
int rc = nKrawall::FetchURL( fullAuthority, "?query=methods", answer );
728
return ReportAuthorityError( tOutput( "$login_error_invalidurl_notfound", authority ) );
737
methods.ReadLine( answer );
740
if ( rc != 200 || id != "methods" )
742
return ReportAuthorityError( tOutput( "$login_error_nomethodlist", authority, rc, id + " " + methods ) );
746
method.method = nKrawall::nMethod::BestMethod(
748
clientSupportedMethods
751
// check whether a method can be found
752
if ( method.method.Len() <= 1 )
754
return ReportAuthorityError(
755
tOutput( "$login_error_nomethod",
756
clientSupportedMethods,
757
nKrawall::nMethod::SupportedMethods(),
763
// fetch md5 prefix and suffix
765
std::ostringstream query;
766
query << "?query=params";
767
query << "&method=" << nKrawall::EncodeString( method.method );
768
std::ostringstream data;
769
int rc = nKrawall::FetchURL( fullAuthority, query.str().c_str(), data );
775
return ReportAuthorityError( tOutput( "$login_error_invalidurl_notfound", authority ) );
778
return ReportAuthorityError( tOutput( "$login_error_nomethodproperties", authority, rc, data.str().c_str() ) );
781
// read the properties
782
std::istringstream read( data.str() );
783
method = nKrawall::nMethod( static_cast< char const * >( method.method ), read );
789
// fetches info from local authority
790
bool nLoginProcess::FetchInfoFromAuthorityLocal()
792
// try yo find a better method
793
if ( !nKrawall::nMethod::BestLocalMethod(
794
clientSupportedMethods,
799
return ReportAuthorityError(
800
tOutput( "$login_error_nomethod",
801
clientSupportedMethods,
802
nKrawall::nMethod::SupportedMethods(),
803
nKrawall::nMethod::SupportedMethods() )
810
// that function again triggers the following foreground action that queries
811
// the credentials from the client. This object then goes to sleep,
812
// waiting for a client answer or logout, whichever comes first.
813
void nLoginProcess::QueryFromClient()
815
// check whether the user disappeared by now (this is run in the main thread,
816
// so no risk of the user disconnecting while the function runs)
817
int userID = sn_UserID( user );
821
// create a random salt value
822
nKrawall::RandomSalt(salt);
824
// send the salt value and the username to the
825
nMessage *m = tNEW(nMessage)(::nPasswordRequest);
826
nKrawall::WriteSalt(salt, *m);
828
*m << static_cast<tString>(message);
829
*m << nLoginPersistence::Find( userID ).userAuthFailedLastTime;
838
// well, then we wait for the answer.
839
con << tOutput( "$login_message_responded", userID, username, method.method, message );
842
// authentication data received from the client is processed here:
843
void nLoginProcess::ProcessClientAnswer( nMessage & m )
847
// read password and username from remote
848
nKrawall::ReadScrambledPassword(m, hash);
851
username.NetFilter();
865
// read the server address the client used for scrambling
868
// sanity check it, of course :)
869
if ( !CheckServerAddress( m ) )
871
// no use going on, the server address won't match, password checking will fail.
877
serverAddress = sn_GetMyAddress();
879
if ( method.method != "bmd5" )
881
con << "WARNING, client did not send the server address. Password checks may fail.\n";
886
nMemberFunctionRunner::ScheduleMayBlock( *this, &nLoginProcess::Authorize, authority != "" );
889
static bool sn_trustLAN = false;
890
static tSettingItem< bool > sn_TrustLANConf( "TRUST_LAN", sn_trustLAN );
892
// sanity check the server address
893
bool nLoginProcess::CheckServerAddress( nMessage & m )
895
// check whether we can read our IP from the socket
896
nSocket const * socket = sn_Connections[m.SenderID()].socket;
899
tString compareAddress = socket->GetAddress().ToString();
900
if ( !compareAddress.StartsWith("*") && compareAddress == serverAddress )
902
// everything is fine, adresses match
907
// check the incoming address, clients from the LAN should be safe
911
sn_GetAdr( m.SenderID(), peerAddress );
912
if ( sn_IsLANAddress( peerAddress ) && sn_IsLANAddress( serverAddress ) )
918
if ( sn_GetMyAddress() == serverAddress )
924
tString hisServerAddress = serverAddress;
925
serverAddress = sn_GetMyAddress();
927
// if we don't know our own address,
928
if ( sn_GetMyAddress().StartsWith("*") )
930
// reject authentication.
931
return ReportAuthorityError( tOutput("$login_error_pharm", hisServerAddress, sn_GetMyAddress() ) );
934
// reject authentication.
935
return ReportAuthorityError( tOutput("$login_error_pharm", hisServerAddress, sn_GetMyAddress() ) );
938
// and here we go again: a background task talks with the authority
939
// and determines whether the client is authorized or not.
940
void nLoginProcess::Authorize()
946
error = tOutput("$login_error_aborted");
950
if ( !tRecorder::IsPlayingBack() )
952
nKrawall::CheckScrambledPassword( *this, *this );
955
// record and playback result (required because on playback, a new
956
// salt is generated and this way, a recoding does not contain ANY
957
// exploitable information for password theft: the scrambled password
958
// stored in the incoming network stream has an unknown salt value. )
959
static char const * section = "AUTH_RESULT";
960
tRecorder::Playback( section, username );
961
tRecorder::Playback( section, success );
962
tRecorder::Playback( section, authority );
963
tRecorder::Playback( section, error );
964
tRecorder::Record( section, username );
965
tRecorder::Record( section, success );
966
tRecorder::Record( section, authority );
967
tRecorder::Record( section, error );
973
// the finish task can also be triggered any time by this function:
974
void nLoginProcess::Abort()
976
nMemberFunctionRunner::ScheduleForeground( *this, &nLoginProcess::Finish );
979
// which, when finished, triggers the foreground task of updating the
980
// game state and informing the client of the success of the operation.
981
void nLoginProcess::Finish()
983
// again, userID is safe in this function
984
int userID = sn_UserID( user );
988
// decorate console with correct sender ID
989
nCurrentSenderID currentSender( userID );
991
// store success for next time
992
nLoginPersistence::Find( userID ).userAuthFailedLastTime = !success;
994
// remove this decorator from public view
997
if (S_LoginResultCallback)
998
(*S_LoginResultCallback)( *this );
1004
static void sn_Reset(){
1005
int userID = nCallbackLoginLogout::User();
1007
// kill/detach pending login process
1008
nLoginProcess * process = nLoginProcess::Find( userID );
1016
static nCallbackLoginLogout reset(&sn_Reset);
1018
#endif // KRAWALL_SERVER
1020
void nAuthentication::HandlePasswordAnswer(nMessage& m)
1022
#ifdef KRAWALL_SERVER
1023
// find login pricess
1024
nLoginProcess * process = nLoginProcess::Find( m.SenderID() );
1026
// and delegate to it
1029
process->ProcessClientAnswer( m );
1034
// on the server: request user authentification from login slot
1035
bool nAuthentication::RequestLogin(const tString & authority, const tString& username, nNetObject & user, const tOutput& message )
1037
#ifdef KRAWALL_SERVER
1038
int userID = user.Owner();
1044
con << tOutput( "$login_message_requested", userID, username, authority );
1046
// do nothing if there is another login in process for that client
1047
if ( nLoginProcess::Find( userID ) )
1052
// trigger function cascade bouncing between threads
1053
(new nLoginProcess( userID ))->Init( authority, username, user, tString(message) );
1059
//! call when you have some time for lengthy authentication queries to servers
1060
void nAuthentication::OnBreak()
1062
#ifdef KRAWALL_SERVER
1063
nMemberFunctionRunnerTemplate< nLoginProcess >::OnBreak();