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