00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include "nKrawall.h"
00036 #include "tString.h"
00037 #include "tConsole.h"
00038 #include "nNetwork.h"
00039 #include "tConfiguration.h"
00040 #include "tArray.h"
00041
00042 #include <string>
00043 #include <vector>
00044 #include <map>
00045
00046 static nKrawall::nMethod sn_bmd5("bmd5"), sn_md5("md5");
00047
00048 static tSettingItem< tString > sn_md5Prefix( "MD5_PREFIX", sn_md5.prefix );
00049 static tSettingItem< tString > sn_md5Suffix( "MD5_SUFFIX", sn_md5.suffix );
00050
00052 nKrawall::nMethod const * const * nKrawall::nMethod::LocalMethods()
00053 {
00054 static nMethod const * methods[] =
00055 {
00056 &sn_md5,
00057 &sn_bmd5,
00058 NULL
00059 };
00060
00061 return methods;
00062 }
00063
00064 #ifdef KRAWALL_SERVER
00065
00066 #include <libxml/nanohttp.h>
00067
00068
00069 struct nLogin
00070 {
00071 tString authority_;
00072
00073
00074 std::map< tString, nKrawall::nScrambledPassword > scrambledPasswords_;
00075
00076
00077 tAccessLevel accessLevel_;
00078
00079
00080 tString password_;
00081
00082 static bool FixOK( tString const & fix )
00083 {
00084 return fix.StrPos( "%" ) < 0;
00085 }
00086
00087 nLogin( char const * authority, tString const & password, tString const & username, tAccessLevel accessLevel )
00088 : authority_( authority ), accessLevel_( accessLevel )
00089 {
00090
00091
00092 nKrawall::nMethod const * const * run = nKrawall::nMethod::LocalMethods();
00093 while ( * run )
00094 {
00095 if ( username.Len() <= 1 &&
00096 ( !FixOK( (*run)->prefix ) || !FixOK( (*run)->suffix ) )
00097 )
00098 {
00099
00100 password_ = password;
00101 }
00102 else
00103 {
00104 (*run)->ScramblePassword( nKrawall::nScrambleInfo( username ), password, scrambledPasswords_[ (*run)->method ] );
00105 }
00106
00107 ++run;
00108 }
00109 }
00110
00111
00112 bool CheckPassword( nKrawall::nScrambleInfo const & info, nKrawall::nMethod const & method, nKrawall::nSalt const & salt, nKrawall::nScrambledPassword const & hash, tString & error ) const
00113 {
00114
00115 std::map< tString, nKrawall::nScrambledPassword >::const_iterator scrambled = scrambledPasswords_.find( method.method );
00116 if ( scrambled != scrambledPasswords_.end() )
00117 {
00118
00119 nKrawall::nMethod const * const * run = nKrawall::nMethod::LocalMethods();
00120 while ( * run )
00121 {
00122 nKrawall::nMethod const & compare = **run;
00123 if ( compare.method == method.method && !nKrawall::nMethod::Equal(compare, method ) )
00124 {
00125 error = tOutput( "$login_error_methodmismatch" );
00126 return false;
00127 }
00128
00129 ++run;
00130 }
00131
00132
00133 nKrawall::nScrambledPassword scrambledCorrect;
00134 method.ScrambleWithSalt( info, (*scrambled).second, salt, scrambledCorrect );
00135 bool ret = nKrawall::ArePasswordsEqual( hash, scrambledCorrect );
00136 if ( !ret )
00137 {
00138 error = tOutput( "$login_error_local_password", info.username );
00139 }
00140 return ret;
00141 }
00142 else
00143 {
00144 if ( password_ == "" )
00145 {
00146 error = "Internal error, local method not found, and no plaintext password stored.";
00147 return false;
00148 }
00149
00150
00151 nKrawall::nScrambledPassword scrambled, correctHash;
00152 method.ScramblePassword( info, password_, scrambled );
00153 method.ScrambleWithSalt( info, scrambled, salt, correctHash );
00154
00155 bool ret = nKrawall::ArePasswordsEqual( hash, correctHash );
00156 if ( !ret )
00157 {
00158 error = tOutput( "$login_error_local_password", info.username );
00159 }
00160 return ret;
00161 }
00162
00163 return false;
00164 }
00165
00166 nLogin()
00167 {
00168 }
00169 };
00170
00171 typedef std::map< tString, nLogin > nLoginMap;
00172
00173
00174 static nLoginMap sn_exactLogins;
00175
00176
00177 static nLoginMap sn_partialLogins;
00178
00179
00180 static void sn_ReadPassword( std::istream & s )
00181 {
00182 tString username, password;
00183 s >> username;
00184 if ( !s.good() )
00185 {
00186 con << tOutput( "$local_user_syntax" );
00187 return;
00188 }
00189 tConfItemBase::EatWhitespace(s);
00190 password.ReadLine(s);
00191 if ( password == "" )
00192 {
00193 con << tOutput( "$local_user_syntax" );
00194 return;
00195 }
00196
00197 sn_exactLogins[ username ] = nLogin( "", password, username, tAccessLevel_Local );
00198 }
00199
00200 static tConfItemFunc sn_kpa( "LOCAL_USER", sn_ReadPassword );
00201 static tAccessLevelSetter sn_kpal( sn_kpa, tAccessLevel_Owner );
00202
00203
00204 static void sn_ReadTeamPassword( std::istream & s )
00205 {
00206 tString username, password;
00207 s >> username;
00208 if ( !s.good() )
00209 {
00210 con << tOutput( "$local_team_syntax" );
00211 return;
00212 }
00213 tConfItemBase::EatWhitespace(s);
00214 password.ReadLine(s);
00215 if ( password == "" )
00216 {
00217 con << tOutput( "$local_team_syntax" );
00218 return;
00219 }
00220
00221 sn_partialLogins[ username ] = nLogin( tString("L_TEAM_") + username, password, tString(""), tAccessLevel_TeamMember );
00222 }
00223
00224 static tConfItemFunc sn_kta( "LOCAL_TEAM", sn_ReadTeamPassword );
00225 static tAccessLevelSetter sn_ktal( sn_kta, tAccessLevel_Owner );
00226
00227
00228 nLoginMap::iterator sn_FindLoginIterator( tString const & username, nLoginMap * & map, bool exact = false )
00229 {
00230
00231 map = &sn_exactLogins;
00232 nLoginMap::iterator found = sn_exactLogins.find( username );
00233 if ( found != sn_exactLogins.end() )
00234 {
00235 return found;
00236 }
00237
00238 map = &sn_partialLogins;
00239 for( int i = username.Len(); i >= 1; --i )
00240 {
00241 tString partial = username.SubStr( 0, i );
00242 nLoginMap::iterator found = map->find( partial );
00243 if ( found != map->end() )
00244 {
00245 return found;
00246 }
00247
00248 if ( exact )
00249 {
00250 return map->end();
00251 }
00252 }
00253
00254 return map->end();
00255 }
00256
00257
00258 nLogin const * sn_FindLogin( tString const & username )
00259 {
00260
00261 nLoginMap * map;
00262 nLoginMap::iterator found = sn_FindLoginIterator( username, map );
00263 if ( found != map->end() )
00264 {
00265 return &found->second;
00266 }
00267
00268 return 0;
00269 }
00270
00271
00272 static void sn_ReadPasswordRemove( std::istream & s )
00273 {
00274 tString username;
00275 s >> username;
00276 nLoginMap * map;
00277 nLoginMap::iterator found = sn_FindLoginIterator( username, map, true );
00278 if ( found != map->end() )
00279 {
00280 map->erase( found );
00281 con << tOutput( "$md5_password_removed", username );
00282 }
00283 else
00284 {
00285 con << tOutput( "$md5_password_remove_notfound", username );
00286 }
00287 }
00288
00289 static tConfItemFunc sn_kpr( "USER_REMOVE", sn_ReadPasswordRemove );
00290
00291
00292 void nKrawall::CheckScrambledPassword( nCheckResultBase & result,
00293 nPasswordCheckData const & data )
00294 {
00295
00296 nSalt salt = data.salt;
00297 data.method.ScrambleSalt( salt, data.serverAddress );
00298
00299
00300 if ( result.authority.Len() <= 1 )
00301 {
00302
00303 nLogin const * login = sn_FindLogin( result.username );
00304 if ( !login )
00305 {
00306 result.success = false;
00307 result.error = tOutput( "$login_error_local_nouser", result.username );
00308 return;
00309 }
00310
00311
00312 result.authority = login->authority_;
00313
00314
00315 result.success = login->CheckPassword( nScrambleInfo( result.username ), data.method, salt, data.hash, result.error );
00316 result.accessLevel = login->accessLevel_;
00317
00318 return;
00319 }
00320 else
00321 {
00322
00323 std::ostringstream request;
00324
00325 request << "?query=check";
00326 request << "&method=" << EncodeString( data.method.method );
00327 request << "&user=" << EncodeString( result.username );
00328 request << "&salt=" << EncodeScrambledPassword( salt );
00329 request << "&hash=" << EncodeScrambledPassword( data.hash );
00330
00331
00332 std::stringstream content;
00333 int rc = FetchURL( data.fullAuthority, request.str().c_str(), content );
00334
00335 if (rc == -1)
00336 {
00337 result.error = tOutput( "$login_error_invalidurl_notfound", result.authority );
00338 result.success = false;
00339 return;
00340 }
00341
00342
00343 char * buf_temp = strdup( content.str().c_str() );
00344 unsigned int len = strlen(buf_temp);
00345
00346
00347 for ( unsigned int i = 0; i < len; ++i )
00348 {
00349 if ( buf_temp[i] == '\n' )
00350 {
00351 buf_temp[i] = ' ';
00352 }
00353 }
00354
00355
00356 while ( len > 0 && buf_temp[len-1] == ' ' )
00357 {
00358 buf_temp[len-1] = 0;
00359 --len;
00360 }
00361
00362 tString buf( buf_temp );
00363 free( buf_temp );
00364
00365
00366 if ( rc != 200 ) {
00367 result.success = false;
00368 switch ( rc )
00369 {
00370 case 404:
00371 result.error = tOutput( "$login_error_nouser", buf );
00372 break;
00373 case 403:
00374 case 401:
00375 result.error = tOutput( "$login_error_password", buf );
00376 break;
00377 default:
00378 result.error = tOutput( "$login_error_unknown", rc, buf );
00379 break;
00380 }
00381
00382 return;
00383 }
00384
00385
00386 tString ret;
00387 content >> ret;
00388 tToLower( ret );
00389
00390
00391 if ( ret == "unknown_user" )
00392 {
00393 result.error = tOutput( "$login_error_nouser", buf );
00394 return;
00395 }
00396
00397 if ( ret == "password_fail" )
00398 {
00399 result.error = tOutput( "$login_error_password", buf );
00400 return;
00401 }
00402
00403 if ( ret != "password_ok" )
00404 {
00405 result.error << tOutput( "$login_error_unexpected_answer", "PASSWORD_OK ...", buf );
00406 return;
00407 }
00408
00409
00410 tString fullUserName;
00411 fullUserName.ReadLine( content );
00412
00413
00414 if ( fullUserName != "" )
00415 {
00416 tString claimedAuthority;
00417 SplitUserName( fullUserName, result.username, claimedAuthority );
00418 if ( claimedAuthority != result.authority )
00419 {
00420 result.error << tOutput( "$login_error_unexpected_answer",
00421 tString("PASSWORD_OK ") + result.username + "@" + result.authority,
00422 buf );
00423 return;
00424 }
00425 }
00426
00427
00428 while( true )
00429 {
00430 tString blurb;
00431 blurb.ReadLine( content );
00432 if ( content.eof() || content.fail() )
00433 {
00434 break;
00435 }
00436 result.blurb.push_back( blurb );
00437 }
00438
00439 result.accessLevel = tAccessLevel_Remote;
00440 result.success = true;
00441 return;
00442 }
00443 }
00444
00445 int nKrawall::FetchURL( tString const & authority, char const * query, std::ostream & target, int maxlen )
00446 {
00447
00448 std::ostringstream fullURL;
00449 fullURL << "http://" << authority << "/armaauth/0.1";
00450 fullURL << query;
00451
00452
00453
00454
00455
00456 void * ctxt = xmlNanoHTTPOpen( fullURL.str().c_str(), NULL);
00457 if (ctxt == NULL)
00458 {
00459 return -1;
00460 }
00461
00462 int rc = xmlNanoHTTPReturnCode(ctxt);
00463
00464
00465 char buf[1000];
00466 buf[0] = 0;
00467 unsigned int len = 1;
00468 while ( len > 0 && maxlen > 0 )
00469 {
00470 int max = sizeof(buf);
00471 if ( max > maxlen )
00472 max = maxlen;
00473 len = xmlNanoHTTPRead( ctxt, &buf, max );
00474 target.write( buf, len );
00475 maxlen -= len;
00476 }
00477
00478 xmlNanoHTTPClose(ctxt);
00479
00480 return rc;
00481 }
00482
00483 #ifdef KRAWALL_SERVER_LEAGUE
00484
00485 static nKrawall::nScrambledPassword key =
00486 {
00487 13, 12, 12, 12, 12, 12, 12, 12
00488 };
00489
00490
00491
00492 const nKrawall::nScrambledPassword& nKrawall::SecretLeagueKey()
00493 {
00494 return key;
00495 }
00496
00497
00498 void nKrawall::MasterFrag(const tString &killer, const tString& victim)
00499 {
00500 con << killer << " killed " << victim << "\n";
00501
00502 }
00503
00504
00505
00506
00507 void nKrawall::MasterRoundEnd(const tString* players, int numPlayers)
00508 {
00509 if (numPlayers > 1)
00510 {
00511 con << players[numPlayers-1] << " survived over ";
00512 for (int i = numPlayers-2; i>=0; i--)
00513 {
00514 con << players[i];
00515 if (i > 0)
00516 con << " and ";
00517 }
00518 con << ".\n";
00519 }
00520
00521 }
00522
00523
00524
00525
00526 bool nKrawall::IsFromKrawall(tString& adress, unsigned int port)
00527 {
00528 return (adress.Len() > 3 &&
00529 !strncmp(adress, "127.0.0", 7));
00530 }
00531
00532
00533
00534 bool nKrawall::RequireMasterLogin(tString& adress, unsigned int port)
00535 {
00536 return (adress.Len() > 3 &&
00537 !strncmp(adress, "127.0.0", 7));
00538 }
00539
00540 #endif
00541 #endif