1 /*
2 
3 *************************************************************************
4 
5 ArmageTron -- Just another Tron Lightcycle Game in 3D.
6 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
7 
8 **************************************************************************
9 
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.
14 
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.
19 
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.
23 
24 ***************************************************************************
25 
26 */
27 
28 #include "nAuthentication.h"
29 #include "tMemManager.h"
30 #include "tToDo.h"
31 #include "tLocale.h"
32 #include "tRecorder.h"
33 #include "tSysTime.h"
34 
35 #include "nNetwork.h"
36 #include "nNetObject.h"
37 #include "nSocket.h"
38 #include "nServerInfo.h"
39 
40 #include <memory>
41 #include <string>
42 #include <string.h>
43 #include <deque>
44 
45 #ifdef HAVE_LIBZTHREAD
46 #include <zthread/Thread.h>
47 #include <zthread/LockedQueue.h>
48 //#include <zthread/ClassLockable.h>
49 #include <zthread/FastMutex.h>
50 #include <zthread/FastRecursiveMutex.h>
51 #include <zthread/Guard.h>
52 // #include <zthread/SynchronousExecutor.h>
53 #include <zthread/ThreadedExecutor.h>
54 typedef ZThread::ThreadedExecutor nExecutor;
55 //typedef ZThread::SynchronousExecutor nExecutor;
56 typedef ZThread::FastMutex nMutex;
57 #define nQueue ZThread::LockedQueue
58 #elif defined(HAVE_PTHREAD)
59 #include "pthread-binding.h"
60 typedef tPThreadMutex nMutex;
61 #define nQueue tPThreadQueue
62 #else
63 typedef tNonMutex nMutex;
64 #endif
65 
66 bool sn_supportRemoteLogins = false;
67 
68 // authority black and whitelists
69 static tString sn_AuthorityBlacklist, sn_AuthorityWhitelist;
70 tConfItemLine  sn_AuthorityBlacklistConf( "AUTHORITY_BLACKLIST", sn_AuthorityBlacklist );
71 tConfItemLine  sn_AuthorityWhitelistConf( "AUTHORITY_WHITELIST", sn_AuthorityWhitelist );
72 
73 #ifdef DEBUG
74 // list of authorities that get accepted as valid authorities, no questions asked
75 static tString sn_AuthorityNoCheck;
76 tConfItemLine  sn_AuthorityNoCheckConf( "AUTHORITY_NO_CHECK", sn_AuthorityNoCheck );
77 #endif
78 
79 static nAuthentication::UserPasswordCallback* S_UserPasswordCallback = NULL;
80 static nAuthentication::LoginResultCallback*  S_LoginResultCallback  = NULL;
81 
82 // let the game register the callbacks
SetUserPasswordCallback(nAuthentication::UserPasswordCallback * callback)83 void nAuthentication::SetUserPasswordCallback(nAuthentication::UserPasswordCallback* callback)
84 {
85     S_UserPasswordCallback = callback;
86 }
87 
SetLoginResultCallback(nAuthentication::LoginResultCallback * callback)88 void nAuthentication::SetLoginResultCallback (nAuthentication::LoginResultCallback* callback)
89 {
90     S_LoginResultCallback = callback;
91 }
92 
93 // network handler declarations
94 
95 static nDescriptor nPasswordRequest(40, &nAuthentication::HandlePasswordRequest, "password_request");
96 
97 static nDescriptor nPasswordAnswer(41, &nAuthentication::HandlePasswordAnswer, "password_answer");
98 
99 // password request and answer
100 static nKrawall::nPasswordRequest sn_request;
101 static nKrawall::nPasswordAnswer sn_answer;
102 static nKrawall::nSalt sn_salt;
103 static int s_inUse = false;
104 
105 // finish the request for username and password
FinishHandlePasswordRequest()106 static void FinishHandlePasswordRequest()
107 {
108     nKrawall::nScrambledPassword egg;
109 
110     // if the callback exists, get the scrambled password of the wanted user
111     if (S_UserPasswordCallback)
112         (*S_UserPasswordCallback)( sn_request, sn_answer );
113 
114     // scramble the salt with the server address
115     sn_GetAdr( 0, sn_answer.serverAddress );
116     sn_request.ScrambleSalt( sn_salt, sn_answer.serverAddress );
117 
118     // scramble it with the given salt
119     sn_request.ScrambleWithSalt( nKrawall::nScrambleInfo(sn_answer.username), sn_answer.scrambled, sn_salt, egg);
120 
121     // destroy the original password
122     sn_answer.scrambled.Clear();
123 
124     // and send it back
125     nMessage *ret = tNEW(nMessage)(nPasswordAnswer);
126     nKrawall::WriteScrambledPassword(egg, *ret);
127     *ret << sn_answer.username;
128     *ret << sn_answer.aborted;
129     *ret << sn_answer.automatic;
130     *ret << sn_answer.serverAddress;
131     ret->Send(0);
132 
133     s_inUse = false;
134 }
135 
136 // receive a password request
HandlePasswordRequest(nMessage & m)137 void nAuthentication::HandlePasswordRequest(nMessage& m)
138 {
139     if (m.SenderID() > 0 || sn_GetNetState() != nCLIENT)
140         Cheater(m.SenderID());
141 
142     sn_answer = nKrawall::nPasswordAnswer();
143     sn_request = nKrawall::nPasswordRequest();
144 
145     // already in the process: return without answer
146     if ( s_inUse )
147         return;
148     s_inUse = true;
149 
150     // read salt and username from the message
151     ReadSalt(m, sn_salt);
152 
153     // read the username as raw as sanely possible
154     m.ReadRaw(sn_answer.username);
155     sn_answer.username.NetFilter();
156 
157     m >> sn_request.message;
158     if (!m.End())
159     {
160         m >> sn_request.failureOnLastTry;
161     }
162     else
163     {
164         sn_request.failureOnLastTry = true;
165     }
166     if (!m.End())
167     {
168         // read method, prefix and suffiox
169         m >> sn_request.method;
170         m.ReadRaw(sn_request.prefix);
171         m.ReadRaw(sn_request.suffix);
172         sn_request.prefix.NetFilter();
173         sn_request.suffix.NetFilter();
174     }
175     else
176     {
177         // clear them
178         sn_request.method = "bmd5";
179         sn_request.prefix = "";
180         sn_request.suffix = "";
181     }
182 
183     // postpone the answer for a better opportunity since it
184     // most likely involves opening a menu and waiting a while (and we
185     // are right now in the process of fetching network messages...)
186     st_ToDo(&FinishHandlePasswordRequest);
187 }
188 
189 #ifdef KRAWALL_SERVER
190 
sn_UserID(nNetObject * o)191 static int sn_UserID( nNetObject * o )
192 {
193     if ( !o )
194     {
195         return -1;
196     }
197     return o->Owner();
198 }
199 
200 class nLoginProcess;
201 
202 //! persistent information between login processes
203 class nLoginPersistence:
204     public nMachineDecorator
205 {
206     friend class nLoginProcess;
207 
nLoginPersistence(int userID)208     nLoginPersistence( int userID )
209     : nMachineDecorator( nMachine::GetMachine( userID ) ),
210       userAuthFailedLastTime( false )
211     {
212     }
213 
Find(int userID)214     static nLoginPersistence & Find( int userID )
215     {
216         nMachine & machine = nMachine::GetMachine( userID );
217         nLoginPersistence * ret = machine.GetDecorator< nLoginPersistence >();
218         if ( !ret )
219         {
220             ret = new nLoginPersistence( userID );
221         }
222 
223         return *ret;
224     }
225 
OnDestroy()226     virtual void OnDestroy()
227     {
228         delete this;
229     }
230 
231     bool userAuthFailedLastTime;
232 };
233 
234 //! template that runs void member functions of reference countable objects
235 template< class T > class nMemberFunctionRunnerTemplate
236 #ifdef HAVE_LIBZTHREAD
237     : public ZThread::Runnable
238 #endif
239 {
240 private:
241 #if defined(HAVE_PTHREAD) && !defined(HAVE_LIBZTHREAD)
DoCall(void * o)242     static void* DoCall( void *o ) {
243         nMemberFunctionRunnerTemplate * functionRunner = (nMemberFunctionRunnerTemplate*) o;
244         ( (functionRunner->object_)->*(functionRunner->function_) )();
245     }
246 #endif
247 public:
nMemberFunctionRunnerTemplate(T & object,void (T::* function)())248     nMemberFunctionRunnerTemplate( T & object, void (T::*function)() )
249     : object_( &object ), function_( function )
250     {
251     }
252 
253     // runs the function
run()254     void run()
255     {
256         (object_->*function_)();
257     }
258 
259     //! schedule a task for execution at the next convenient break, between game rounds for example
ScheduleBreak(T & object,void (T::* function)())260     static void ScheduleBreak( T & object, void (T::*function)()  )
261     {
262         pendingForBreak_.push_back( nMemberFunctionRunnerTemplate( object, function ) );
263     }
264 
265     //! schedule a task for execution in a background thread
ScheduleBackground(T & object,void (T::* function)())266     static void ScheduleBackground( T & object, void (T::*function)()  )
267     {
268 #if defined(HAVE_LIBZTHREAD) || defined(HAVE_PTHREAD)
269         // schedule the task into a background thread
270         if ( !tRecorder::IsRunning() )
271         {
272 #if !defined(HAVE_LIBZTHREAD)
273             nMemberFunctionRunnerTemplate<T> * runner = new nMemberFunctionRunnerTemplate<T>( object, function );
274 
275             pthread_t thread;
276             pthread_create(&thread, NULL, (nMemberFunctionRunnerTemplate::DoCall), (void*) runner);
277 #else
278             static nExecutor executor;
279             executor.execute( ZThread::Task( new nMemberFunctionRunnerTemplate( object, function ) ) );
280 #endif
281         }
282         else
283         {
284             // don't start threads when we're recording, just do the task at the next opportunity
285             ScheduleBreak( object, function );
286 
287         }
288 #else
289         // do it when you can without getting interrupted.
290         ScheduleBreak( object, function );
291 #endif
292     }
293 
294     //! schedule a task for execution in the next tToDo call
ScheduleForeground(T & object,void (T::* function)())295     static void ScheduleForeground( T & object, void (T::*function)()  )
296     {
297 #if defined(HAVE_LIBZTHREAD) || defined(HAVE_PTHREAD)
298         Pending().add( nMemberFunctionRunnerTemplate( object, function ) );
299         st_ToDo( FinishAll );
300 #else
301         // execute it immedeately
302         (object.*function)();
303 #endif
304 
305     }
306 
307     // function that calls tasks scheduled for the next break
OnBreak()308     static void OnBreak()
309     {
310         // finish all pending tasks
311         while( pendingForBreak_.size() > 0 )
312         {
313             // sync the network so built up auth requests don't cause connection drops so quickly
314             sn_Receive();
315             nNetObject::SyncAll();
316             tAdvanceFrame();
317             sn_SendPlanned();
318 
319             nMemberFunctionRunnerTemplate & next = pendingForBreak_.front();
320             next.run();
321             pendingForBreak_.pop_front();
322         }
323     }
324 private:
325     //! pointer to the object we should so something with
326     tJUST_CONTROLLED_PTR< T > object_;
327 
328     //! the function to call
329     void (T::*function_)();
330 
331     // taks for the break
332     static std::deque< nMemberFunctionRunnerTemplate > pendingForBreak_;
333 
334 #if defined(HAVE_LIBZTHREAD) || defined(HAVE_PTHREAD)
335     // queue of foreground tasks
Pending()336     static nQueue< nMemberFunctionRunnerTemplate, nMutex > & Pending()
337     {
338         static nQueue< nMemberFunctionRunnerTemplate, nMutex > pending;
339         return pending;
340     }
341 
342     // function that calls them
FinishAll()343     static void FinishAll()
344     {
345         // finish all pending tasks
346         while( Pending().size() > 0 )
347         {
348             nMemberFunctionRunnerTemplate next = Pending().next();
349             next.run();
350         }
351     }
352 #endif
353 };
354 
355 template< class T >
356 std::deque< nMemberFunctionRunnerTemplate<T> >
357 nMemberFunctionRunnerTemplate<T>::pendingForBreak_;
358 
359 // convenience wrapper
360 class nMemberFunctionRunner
361 {
362 public:
363     enum ScheduleType
364     {
365         Break,
366         Foreground,
367         Background
368     };
369 
ScheduleBreak(T & object,void (T::* function)())370     template< class T > static void ScheduleBreak( T & object, void (T::*function)() )
371     {
372         nMemberFunctionRunnerTemplate<T>::ScheduleBreak( object, function );
373     }
374 
ScheduleBackground(T & object,void (T::* function)())375     template< class T > static void ScheduleBackground( T & object, void (T::*function)() )
376     {
377         nMemberFunctionRunnerTemplate<T>::ScheduleBackground( object, function );
378     }
379 
ScheduleForeground(T & object,void (T::* function)())380     template< class T > static void ScheduleForeground( T & object, void (T::*function)() )
381     {
382         nMemberFunctionRunnerTemplate<T>::ScheduleForeground( object, function );
383     }
384 
ScheduleMayBlock(T & object,void (T::* function)(),bool block)385     template< class T > static void ScheduleMayBlock( T & object, void (T::*function)(), bool block )
386     {
387         if ( block )
388         {
389 #if defined(HAVE_LIBZTHREAD) || defined(HAVE_PTHREAD)
390             ScheduleBackground( object, function );
391 #else
392             ScheduleBreak( object, function );
393 #endif
394         }
395         else
396         {
397             ScheduleForeground( object, function );
398         }
399     }
400 };
401 
402 
403 //! manager for logon processes
404 class nLoginProcess:
405     public nMachineDecorator,
406     public nKrawall::nCheckResult,
407     public nKrawall::nPasswordCheckData,
408     public tReferencable< nLoginProcess, nMutex >
409 {
410     // reference counting pointer
411     typedef tJUST_CONTROLLED_PTR< nLoginProcess > SelfPointer;
412 public:
nLoginProcess(int userID)413     nLoginProcess( int userID )
414     : nMachineDecorator( nMachine::GetMachine( userID ) )
415     , checkAddress( true )
416     {
417         // install self reference to keep this object alive
418         selfReference_ = this;
419 
420     // inform the user about delays
421         bool delays = false;
422 #if defined(HAVE_LIBZTHREAD) || defined(HAVE_PTHREAD)
423         delays = tRecorder::IsRunning();
424 #endif
425         if ( delays )
426         {
427             sn_ConsoleOut( tOutput( "$login_message_delayed" ), userID );
428         }
429     }
430 
~nLoginProcess()431     ~nLoginProcess()
432     {
433     }
434 
Find(int userID)435     static nLoginProcess * Find( int userID )
436     {
437         nMachine & machine = nMachine::GetMachine( userID );
438         return machine.GetDecorator< nLoginProcess >();
439     }
440 
441     // OK, authentication goes in several steps. First, we initialize everything
Init(tString const & authority,tString const & username,nNetObject & user,tString const & message)442     void Init( tString const & authority, tString const & username, nNetObject & user, tString const & message )
443     {
444         this->user = &user;
445         this->username = username;
446         this->message = message;
447         this->authority = authority;
448 
449         clientSupportedMethods = sn_Connections[user.Owner()].supportedAuthenticationMethods_;
450 
451         nMemberFunctionRunner::ScheduleMayBlock( *this, &nLoginProcess::FetchInfoFromAuthority, authority != "" );
452     }
453 
454     // That function triggers fetching of authentication relevant data from the authentication
455     // server in this function which is supposed to run in the background:
456     void FetchInfoFromAuthority();
457 
458     // report an authority info query error to the higher level system
ReportAuthorityError(tOutput const & error)459     bool ReportAuthorityError( tOutput const & error )
460     {
461         // prepare failure report
462         this->success = false;
463         this->error = error;
464 
465         Abort();
466 
467         return false;
468     }
469 
470     // that function again triggers the following foreground action that queries
471     // the credentials from the client. This object then goes to sleep,
472     // waiting for a client answer or logout, whichever comes first.
473     void QueryFromClient();
474 
475     // authentication data received from the client is processed here:
476     void ProcessClientAnswer( nMessage & answer );
477 
478     // sanity check the server address
479     bool CheckServerAddress();
480 
481     // and here we go again: a background task talks with the authority
482     // and determines whether the client is authorized or not.
483     void Authorize();
484 
485     // which, when finished, triggers the foreground task of updating the
486     // game state and informing the client of the success of the operation.
487     void Finish();
488 
489     // the finish task can also be triggered any time by this function:
490     void Abort();
491 private:
492     // helper functions
493 
494     // fetches info from remote authority
495     bool FetchInfoFromAuthorityRemote();
496 
497     // fetches info from local authority
498     bool FetchInfoFromAuthorityLocal();
499 
500     tString                message;     //!< message to present to user
501 
502 
503     tString clientSupportedMethods;     //!< methods supported by the client
504 
505 
506     // called when the machine gets destroyed, which happens a bit after
507     // the client logged out. If no process is currently running, destroy the object.
OnDestroy()508     virtual void OnDestroy()
509     {
510         SelfPointer keepAlive( this );
511         selfReference_ = 0;
512     }
513 
514     //! pointer to self to keep the object alive while the machine exists
515     SelfPointer selfReference_;
516 
517     //! address of socket receiving the login message
518     nAddress serverSocketAddress;
519 
520     //! address of login message sender
521     tString peerAddress;
522 
523     //! flag indicating whether the sent server address needs checking
524     bool checkAddress;
525 };
526 
527 
528 
529 // That function triggers fetching of authentication relevant data from the authentication
530 // server in this function which is supposed to run in the background:
FetchInfoFromAuthority()531 void nLoginProcess::FetchInfoFromAuthority()
532 {
533     // set method to defaults
534     method.method = "bmd5";
535     method.prefix = "";
536     method.suffix = "";
537 
538     bool ret = false;
539     if ( !tRecorder::IsPlayingBack() )
540     {
541         if ( authority.Len() <= 1 )
542         {
543             // local logins are easy, handle them first
544             ret = FetchInfoFromAuthorityLocal();
545         }
546         else
547         {
548             // remote logins are harder.
549             ret = FetchInfoFromAuthorityRemote();
550         }
551     }
552 
553     // record and playback result.
554     static char const * section = "AUTH_INFO";
555     tRecorder::Playback( section, ret );
556     tRecorder::Playback( section, method.method );
557     tRecorder::Playback( section, method.prefix );
558     tRecorder::Playback( section, method.suffix );
559     tRecorder::Playback( section, authority );
560     tRecorder::Playback( section, error );
561     tRecorder::Record( section, ret );
562     tRecorder::Record( section, method.method );
563     tRecorder::Record( section, method.prefix );
564     tRecorder::Record( section, method.suffix );
565     tRecorder::Record( section, authority );
566     tRecorder::Record( section, error );
567 
568     if ( !ret )
569     {
570         if ( tRecorder::IsPlayingBack() )
571         {
572             Abort();
573         }
574 
575         return;
576     }
577 
578     // and go on
579     nMemberFunctionRunner::ScheduleForeground( *this, &nLoginProcess::QueryFromClient );
580 }
581 
582 static tSettingItem< bool > sn_supportRemoteLoginsConf( "GLOBAL_ID", sn_supportRemoteLogins );
583 
584 // legal characters in authority hostnames(besides alnum and dots)
sn_IsLegalSpecialChar(char c)585 static bool sn_IsLegalSpecialChar( char c )
586 {
587     switch (c)
588     {
589     case '-': // well, ok, this character actually happens to be in many URLs :)
590     case '+': // these not, but let's consider them legal.
591     case '=':
592     case '_':
593         return true;
594     default:
595         return false;
596     }
597 }
598 
599 // fetches info from remote authority
FetchInfoFromAuthorityRemote()600 bool nLoginProcess::FetchInfoFromAuthorityRemote()
601 {
602     if ( !sn_supportRemoteLogins )
603     {
604         return ReportAuthorityError( tOutput("$login_error_noremote") );
605     }
606 
607     {
608         // the hostname part of the authority should not contain uppercase letters
609 #ifdef DEBUG
610         if ( tIsInList( sn_AuthorityNoCheck, authority ) )
611         {
612             fullAuthority = authority;
613         }
614         else
615 #endif
616         {
617             std::istringstream in( static_cast< const char * >( authority ) );
618             std::ostringstream outShort; // stream for shorthand authority
619             std::ostringstream outFull;  // stream for full authority URL that is to be used for lookups
620             std::ostringstream outDirectory;
621             int c = in.get();
622 
623             // is the authority an abreviation?
624             bool shortcut = true;
625             // does it contain a path?
626             bool hasPath = false;
627 
628             // is the server a raw IP?
629             bool rawIP = true;
630 
631             // which part we're currently parsing
632             bool inHostName = true;
633             bool inPort = false;
634             bool slash = false;
635             int port = 0;
636 
637             while( !in.eof() )
638             {
639                 if ( inHostName )
640                 {
641                     // check validity of hostname part
642                     if ( c == '.' )
643                     {
644                         shortcut = false;
645                     }
646                     else if ( isalnum(c) )
647                     {
648                         c = tolower(c);
649                         if ( !isdigit( c ) )
650                         {
651                             rawIP = false;
652                         }
653                     }
654                     else if ( c == ':' )
655                     {
656                         inPort = true;
657                         inHostName = false;
658                     }
659                     else if ( c == '/' )
660                     {
661                         slash = true;
662                         inHostName = false;
663                     }
664                     else if ( !sn_IsLegalSpecialChar(c) )
665                     {
666                         return ReportAuthorityError( tOutput( "$login_error_invalidurl_illegal_hostname", authority ) );
667                     }
668                 }
669                 else if ( inPort )
670                 {
671                     if ( c == '/' )
672                     {
673                         inPort = false;
674                         slash = true;
675                     }
676                     else if ( !isdigit( c ) )
677                     {
678                         return ReportAuthorityError( tOutput( "$login_error_invalidurl_illegal_port", authority ) );
679                     }
680                     else
681                     {
682                         port *= 10;
683                         port += c - '0';
684                     }
685                 }
686                 else // must be in path
687                 {
688                     if ( c == '/' )
689                     {
690                         if ( slash )
691                         {
692                             return ReportAuthorityError( tOutput( "$login_error_invalidurl_slash", authority ) );
693                         }
694 
695                         slash = true;
696                     }
697                     else
698                     {
699                         if (!isalnum(c) && c != '.' && c != '~' && !sn_IsLegalSpecialChar(c) )
700                         {
701                             return ReportAuthorityError( tOutput( "$login_error_invalidurl_illegal_path", authority )  );
702                         }
703 
704                         slash = false;
705                         hasPath = true;
706                     }
707                 }
708 
709                 // shorthand authority must consist of lowercase letters only
710                 if( inHostName || inPort )
711                 {
712                     outShort.put(tolower(c));
713                     outFull.put( (char) c );
714                 }
715                 else
716                 {
717                     outDirectory.put( (char) c);
718                 }
719 
720 
721                 c = in.get();
722             }
723             if ( slash )
724             {
725                 return ReportAuthorityError( tOutput( "$login_error_invalidurl_slash", authority ) );
726             }
727             if ( port == 80 )
728             {
729                 return ReportAuthorityError( tOutput( "$login_error_invalidurl_defaultport", authority ) );
730             }
731 
732             if ( rawIP )
733             {
734                 return ReportAuthorityError( tOutput( "$login_error_invalidurl_rawip", authority ) );
735             }
736 
737 
738             authority = outShort.str().c_str();
739             fullAuthority = outFull.str().c_str();
740 
741             static const char * def = ".authentication.armagetronad.net";
742 
743             // append default authority path
744             if ( authority.Len() > 1 && shortcut )
745             {
746                 fullAuthority += def;
747             }
748 
749             // check if the pased authority contains the default ending
750             if ( !shortcut && authority.Reverse().StartsWith( tString( def ).Reverse() ) )
751             {
752                 // strip it
753                 authority = authority.SubStr( 0, authority.Len() - strlen( def ) - 1 );
754                 shortcut = true;
755             }
756 
757             if( hasPath )
758             {
759                 fullAuthority += outDirectory.str().c_str();
760                 authority += outDirectory.str().c_str();
761             }
762         }
763 
764         // check for authority in black and whitelist
765         if ( tIsInList( sn_AuthorityBlacklist, authority ) )
766         {
767             return ReportAuthorityError( tOutput( "$login_error_blacklist", authority ) );
768         }
769 
770         if ( sn_AuthorityWhitelist != "" && !tIsInList( sn_AuthorityWhitelist, authority ) )
771         {
772             return ReportAuthorityError( tOutput( "$login_error_whitelist", authority ) );
773         }
774 
775         // try yo find a better method, fetch method list
776         std::stringstream answer;
777         int rc = nKrawall::FetchURL( fullAuthority, "?query=methods", answer );
778 
779         if ( rc == -1 )
780         {
781             return ReportAuthorityError( tOutput( "$login_error_invalidurl_notfound", authority ) );
782         }
783 
784         tString id;
785         answer >> id;
786         tToLower(id);
787 
788         tString methods;
789         std::ws(answer);
790         methods.ReadLine( answer );
791         tToLower(methods);
792 
793         if ( rc != 200 || id != "methods" )
794         {
795             return ReportAuthorityError( tOutput( "$login_error_nomethodlist", authority, rc, id + " " + methods ) );
796         }
797 
798 
799         method.method = nKrawall::nMethod::BestMethod(
800             methods,
801             clientSupportedMethods
802             );
803 
804         // check whether a method can be found
805         if ( method.method.Len() <= 1 )
806         {
807             return ReportAuthorityError(
808                 tOutput( "$login_error_nomethod",
809                          clientSupportedMethods,
810                          nKrawall::nMethod::SupportedMethods(),
811                          methods )
812                 );
813         }
814     }
815 
816     // fetch md5 prefix and suffix
817     {
818         std::ostringstream query;
819         query << "?query=params";
820         query << "&method=" << nKrawall::EncodeString( method.method );
821         std::ostringstream data;
822         int rc = nKrawall::FetchURL( fullAuthority, query.str().c_str(), data );
823 
824         if ( rc != 200 )
825         {
826             if ( rc == -1 )
827             {
828                 return ReportAuthorityError( tOutput( "$login_error_invalidurl_notfound", authority ) );
829             }
830 
831             return ReportAuthorityError( tOutput( "$login_error_nomethodproperties", authority, rc, data.str().c_str() ) );
832         }
833 
834         // read the properties
835         std::istringstream read( data.str() );
836         method = nKrawall::nMethod( static_cast< char const * >( method.method ), read );
837     }
838 
839     return true;
840 }
841 
842 // fetches info from local authority
FetchInfoFromAuthorityLocal()843 bool nLoginProcess::FetchInfoFromAuthorityLocal()
844 {
845     // try yo find a better method
846     if ( !nKrawall::nMethod::BestLocalMethod(
847              clientSupportedMethods,
848              method
849              )
850         )
851     {
852         return ReportAuthorityError(
853             tOutput( "$login_error_nomethod",
854                      clientSupportedMethods,
855                      nKrawall::nMethod::SupportedMethods(),
856                      nKrawall::nMethod::SupportedMethods() )
857             );
858     }
859 
860     return true;
861 }
862 
863 // that function again triggers the following foreground action that queries
864 // the credentials from the client. This object then goes to sleep,
865 // waiting for a client answer or logout, whichever comes first.
QueryFromClient()866 void nLoginProcess::QueryFromClient()
867 {
868     // check whether the user disappeared by now (this is run in the main thread,
869     // so no risk of the user disconnecting while the function runs)
870     int userID = sn_UserID( user );
871     if ( userID <= 0 )
872         return;
873 
874     // create a random salt value
875     nKrawall::RandomSalt(salt);
876 
877     // send the salt value and the username to the
878     nMessage *m = tNEW(nMessage)(::nPasswordRequest);
879     nKrawall::WriteSalt(salt, *m);
880     *m << username;
881     *m << static_cast<tString>(message);
882     *m << nLoginPersistence::Find( userID ).userAuthFailedLastTime;
883 
884     // write method info
885     *m << method.method;
886     *m << method.prefix;
887     *m << method.suffix;
888 
889     m->Send(userID);
890 
891     // well, then we wait for the answer.
892     con << tOutput( "$login_message_responded", userID, username, method.method, message );
893 }
894 
895 // authentication data received from the client is processed here:
ProcessClientAnswer(nMessage & m)896 void nLoginProcess::ProcessClientAnswer( nMessage & m )
897 {
898     success = false;
899 
900     // read password and username from remote
901     nKrawall::ReadScrambledPassword(m, hash);
902 
903     m.ReadRaw(username);
904     username.NetFilter();
905 
906     aborted = false;
907     automatic = false;
908     if ( !m.End() )
909     {
910         m >> aborted;
911     }
912     if ( !m.End() )
913     {
914         m >> automatic;
915     }
916     if (!m.End())
917     {
918         // read the server address the client used for scrambling
919         m >> serverAddress;
920 
921         // sanity check it later
922     }
923     else
924     {
925         serverAddress = sn_GetMyAddress();
926 
927         if ( method.method != "bmd5" )
928         {
929             con << "WARNING, client did not send the server address. Password checks may fail.\n";
930         }
931         else
932         {
933             checkAddress = false;
934         }
935     }
936 
937     // store receiving socket address
938     nSocket const * socket = sn_Connections[m.SenderID()].socket;
939     if ( !socket )
940     {
941         ReportAuthorityError( "Internal error, no receiving socket of authentication message." );
942     }
943     serverSocketAddress = socket->GetAddress();
944 
945     // store peer address
946     sn_GetAdr( m.SenderID(), peerAddress );
947 
948     // and go on
949     nMemberFunctionRunner::ScheduleMayBlock( *this, &nLoginProcess::Authorize, authority != "" );
950 }
951 
952 static bool sn_trustLAN = false;
953 static tSettingItem< bool > sn_TrustLANConf( "TRUST_LAN", sn_trustLAN );
954 
955 // sanity check the server address
CheckServerAddress()956 bool nLoginProcess::CheckServerAddress()
957 {
958     // if no check is requested (only canm happen for old bmd5 protocol), don't check.
959     if ( !checkAddress )
960     {
961         return true;
962     }
963 
964     // serverAddress given from client never can be *.*.*.*:*. This would
965     // give false positive check results because some of the methods below
966     // compare serverAddress to strings that may be *.*.*.*:*, and checking
967     //  serverAddress once here is the safest and easiest way.
968     if ( serverAddress.StartsWith("*") )
969     {
970         return ReportAuthorityError( tOutput("$login_error_pharm_cheap" ) );
971     }
972 
973     // check whether we can read our IP from the socket
974     tString compareAddress = serverSocketAddress.ToString();
975     if ( compareAddress == serverAddress )
976     {
977         // everything is fine, adresses match
978         return true;
979     }
980 
981     // check the incoming address, clients from the LAN should be safe
982     if ( sn_trustLAN )
983     {
984         if ( sn_IsLANAddress( peerAddress ) && sn_IsLANAddress( serverAddress ) )
985         {
986             return true;
987         }
988     }
989 
990     // std::cout << serverAddress;
991 
992     // fetch our server address. First, try the basic networking system.
993     tString trueServerAddress = sn_GetMyAddress();
994 
995     if ( trueServerAddress == serverAddress )
996     {
997         // all's well
998         return true;
999     }
1000 
1001     // if SERVER_DNS is set, the client most likely will connect over that IP. Use it.
1002     if ( sn_GetMyDNSName().Len() > 1 )
1003     {
1004         // resolve DNS. Yes, do this every time someone logs in, IPs can change.
1005         // after all, that's the point of setting SERVER_DNS :)
1006         nAddress address;
1007         address.SetHostname( sn_GetMyDNSName() );
1008 
1009         address.SetPort( serverSocketAddress.GetPort() );
1010 
1011         // transform back to string
1012         trueServerAddress = address.ToString();
1013     }
1014 
1015     if ( trueServerAddress == serverAddress )
1016     {
1017         // all's well
1018         return true;
1019     }
1020 
1021     // Z-Man: can't remember what this swapping is for. Possibly to accept
1022     // the login anyway for debugging purposes.
1023     tString hisServerAddress = serverAddress;
1024     serverAddress = trueServerAddress;
1025 
1026     // reject authentication.
1027     return ReportAuthorityError( tOutput("$login_error_pharm", hisServerAddress, trueServerAddress ) );
1028 }
1029 
1030 // and here we go again: a background task talks with the authority
1031 // and determines whether the client is authorized or not.
Authorize()1032 void nLoginProcess::Authorize()
1033 {
1034     if ( aborted )
1035     {
1036         success = false;
1037 
1038         error = tOutput("$login_error_aborted");
1039     }
1040     else
1041     {
1042         // sanity check it server address
1043         if ( !CheckServerAddress() )
1044         {
1045             // no use going on, the server address won't match, password checking will fail.
1046             return;
1047         }
1048 
1049         if ( !tRecorder::IsPlayingBack() )
1050         {
1051             nKrawall::CheckScrambledPassword( *this, *this );
1052         }
1053 
1054         // record and playback result (required because on playback, a new
1055         // salt is generated and this way, a recoding does not contain ANY
1056         // exploitable information for password theft: the scrambled password
1057         // stored in the incoming network stream has an unknown salt value. )
1058         static char const * section = "AUTH_RESULT";
1059         tRecorder::Playback( section, username );
1060         tRecorder::Playback( section, success );
1061         tRecorder::Playback( section, authority );
1062         tRecorder::Playback( section, error );
1063         tRecorder::Record( section, username );
1064         tRecorder::Record( section, success );
1065         tRecorder::Record( section, authority );
1066         tRecorder::Record( section, error );
1067     }
1068 
1069     Abort();
1070 }
1071 
1072 // the finish task can also be triggered any time by this function:
Abort()1073 void nLoginProcess::Abort()
1074 {
1075     nMemberFunctionRunner::ScheduleBackground( *this, &nLoginProcess::Finish );
1076 }
1077 
1078 // which, when finished, triggers the foreground task of updating the
1079 // game state and informing the client of the success of the operation.
Finish()1080 void nLoginProcess::Finish()
1081 {
1082     // again, userID is safe in this function
1083     int userID = sn_UserID( user );
1084     if ( userID <= 0 )
1085         return;
1086 
1087     // decorate console with correct sender ID
1088     nCurrentSenderID currentSender( userID );
1089 
1090     // store success for next time
1091     nLoginPersistence::Find( userID ).userAuthFailedLastTime = !success;
1092 
1093     // remove this decorator from public view
1094     Remove();
1095 
1096     if (S_LoginResultCallback)
1097         (*S_LoginResultCallback)( *this );
1098 
1099     // bye-bye!
1100     Destroy();
1101 }
1102 
sn_Reset()1103 static void sn_Reset(){
1104     int userID = nCallbackLoginLogout::User();
1105 
1106     // kill/detach pending login process
1107     nLoginProcess * process = nLoginProcess::Find( userID );
1108     if ( process )
1109     {
1110         process->Remove();
1111         process->Destroy();
1112     }
1113 }
1114 
1115 static nCallbackLoginLogout reset(&sn_Reset);
1116 
1117 #endif // KRAWALL_SERVER
1118 
HandlePasswordAnswer(nMessage & m)1119 void nAuthentication::HandlePasswordAnswer(nMessage& m)
1120 {
1121 #ifdef KRAWALL_SERVER
1122     // find login pricess
1123     nLoginProcess * process = nLoginProcess::Find( m.SenderID() );
1124 
1125     // and delegate to it
1126     if ( process )
1127     {
1128         process->ProcessClientAnswer( m );
1129     }
1130 #endif
1131 }
1132 
1133 // on the server: request user authentification from login slot
RequestLogin(const tString & authority,const tString & username,nNetObject & user,const tOutput & message)1134 bool nAuthentication::RequestLogin(const tString & authority, const tString& username, nNetObject & user, const tOutput& message )
1135 {
1136 #ifdef KRAWALL_SERVER
1137     int userID = user.Owner();
1138     if ( userID <= 0 )
1139     {
1140         return false;
1141     }
1142 
1143     con << tOutput( "$login_message_requested", userID, username, authority );
1144 
1145     // do nothing if there is another login in process for that client
1146     if ( nLoginProcess::Find( userID ) )
1147     {
1148         return false;
1149     }
1150 
1151     // trigger function cascade bouncing between threads
1152     (new nLoginProcess( userID ))->Init( authority, username, user, tString(message) );
1153 #endif
1154 
1155     return true;
1156 }
1157 
1158 //! call when you have some time for lengthy authentication queries to servers
OnBreak()1159 void nAuthentication::OnBreak()
1160 {
1161 #ifdef KRAWALL_SERVER
1162     nMemberFunctionRunnerTemplate< nLoginProcess >::OnBreak();
1163 #endif
1164     st_DoToDo();
1165 }
1166 
1167