void Client::sendChangePassword(const std::string &oldpassword, const std::string &newpassword) { LocalPlayer *player = m_env.getLocalPlayer(); if (player == NULL) return; std::string playername = player->getName(); if (m_proto_ver >= 25) { // get into sudo mode and then send new password to server m_password = oldpassword; m_new_password = newpassword; startAuth(choseAuthMech(m_sudo_auth_methods)); } else { std::string oldpwd = translate_password(playername, oldpassword); std::string newpwd = translate_password(playername, newpassword); NetworkPacket pkt(TOSERVER_PASSWORD_LEGACY, 2 * PASSWORD_SIZE); for (u8 i = 0; i < PASSWORD_SIZE; i++) { pkt << (u8) (i < oldpwd.length() ? oldpwd[i] : 0); } for (u8 i = 0; i < PASSWORD_SIZE; i++) { pkt << (u8) (i < newpwd.length() ? newpwd[i] : 0); } Send(&pkt); } }
// check_password_entry(name, entry, password) int ModApiUtil::l_check_password_entry(lua_State *L) { NO_MAP_LOCK_REQUIRED; std::string name = luaL_checkstring(L, 1); std::string entry = luaL_checkstring(L, 2); std::string password = luaL_checkstring(L, 3); if (base64_is_valid(entry)) { std::string hash = translate_password(name, password); lua_pushboolean(L, hash == entry); return 1; } std::string salt; std::string verifier; if (!decode_srp_verifier_and_salt(entry, &verifier, &salt)) { // invalid format warningstream << "Invalid password format for " << name << std::endl; lua_pushboolean(L, false); return 1; } std::string gen_verifier = generate_srp_verifier(name, password, salt); lua_pushboolean(L, gen_verifier == verifier); return 1; }
// get_password_hash(name, raw_password) int ModApiUtil::l_get_password_hash(lua_State *L) { NO_MAP_LOCK_REQUIRED; std::string name = luaL_checkstring(L, 1); std::string raw_password = luaL_checkstring(L, 2); std::string hash = translate_password(name, raw_password); lua_pushstring(L, hash.c_str()); return 1; }
void Client::sendChangePassword(const std::string &oldpassword, const std::string &newpassword) { Player *player = m_env.getLocalPlayer(); if(player == NULL) return; std::string playername = player->getName(); std::string oldpwd = translate_password(playername, oldpassword); std::string newpwd = translate_password(playername, newpassword); MSGPACK_PACKET_INIT(TOSERVER_CHANGE_PASSWORD, 2); PACK(TOSERVER_CHANGE_PASSWORD_OLD, oldpwd); PACK(TOSERVER_CHANGE_PASSWORD_NEW, newpwd); // Send as reliable Send(0, buffer, true); }
void Client::startAuth(AuthMechanism chosen_auth_mechanism) { m_chosen_auth_mech = chosen_auth_mechanism; switch (chosen_auth_mechanism) { case AUTH_MECHANISM_FIRST_SRP: { // send srp verifier to server std::string verifier; std::string salt; generate_srp_verifier_and_salt(getPlayerName(), m_password, &verifier, &salt); NetworkPacket resp_pkt(TOSERVER_FIRST_SRP, 0); resp_pkt << salt << verifier << (u8)((m_password.empty()) ? 1 : 0); Send(&resp_pkt); break; } case AUTH_MECHANISM_SRP: case AUTH_MECHANISM_LEGACY_PASSWORD: { u8 based_on = 1; if (chosen_auth_mechanism == AUTH_MECHANISM_LEGACY_PASSWORD) { m_password = translate_password(getPlayerName(), m_password); based_on = 0; } std::string playername_u = lowercase(getPlayerName()); m_auth_data = srp_user_new(SRP_SHA256, SRP_NG_2048, getPlayerName().c_str(), playername_u.c_str(), (const unsigned char *) m_password.c_str(), m_password.length(), NULL, NULL); char *bytes_A = 0; size_t len_A = 0; SRP_Result res = srp_user_start_authentication( (struct SRPUser *) m_auth_data, NULL, NULL, 0, (unsigned char **) &bytes_A, &len_A); FATAL_ERROR_IF(res != SRP_OK, "Creating local SRP user failed."); NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_A, 0); resp_pkt << std::string(bytes_A, len_A) << based_on; Send(&resp_pkt); break; } case AUTH_MECHANISM_NONE: break; // not handled in this method } }
void Client::step(float dtime) { DSTACK(FUNCTION_NAME); // Limit a bit if(dtime > 2.0) dtime = 2.0; if(m_ignore_damage_timer > dtime) m_ignore_damage_timer -= dtime; else m_ignore_damage_timer = 0.0; m_animation_time += dtime; if(m_animation_time > 60.0) m_animation_time -= 60.0; m_time_of_day_update_timer += dtime; ReceiveAll(); /* Packet counter */ { float &counter = m_packetcounter_timer; counter -= dtime; if(counter <= 0.0) { counter = 20.0; infostream << "Client packetcounter (" << m_packetcounter_timer << "):"<<std::endl; m_packetcounter.print(infostream); m_packetcounter.clear(); } } // UGLY hack to fix 2 second startup delay caused by non existent // server client startup synchronization in local server or singleplayer mode static bool initial_step = true; if (initial_step) { initial_step = false; } else if(m_state == LC_Created) { float &counter = m_connection_reinit_timer; counter -= dtime; if(counter <= 0.0) { counter = 2.0; LocalPlayer *myplayer = m_env.getLocalPlayer(); FATAL_ERROR_IF(myplayer == NULL, "Local player not found in environment."); u16 proto_version_min = g_settings->getFlag("send_pre_v25_init") ? CLIENT_PROTOCOL_VERSION_MIN_LEGACY : CLIENT_PROTOCOL_VERSION_MIN; if (proto_version_min < 25) { // Send TOSERVER_INIT_LEGACY // [0] u16 TOSERVER_INIT_LEGACY // [2] u8 SER_FMT_VER_HIGHEST_READ // [3] u8[20] player_name // [23] u8[28] password (new in some version) // [51] u16 minimum supported network protocol version (added sometime) // [53] u16 maximum supported network protocol version (added later than the previous one) char pName[PLAYERNAME_SIZE]; char pPassword[PASSWORD_SIZE]; memset(pName, 0, PLAYERNAME_SIZE * sizeof(char)); memset(pPassword, 0, PASSWORD_SIZE * sizeof(char)); std::string hashed_password = translate_password(myplayer->getName(), m_password); snprintf(pName, PLAYERNAME_SIZE, "%s", myplayer->getName()); snprintf(pPassword, PASSWORD_SIZE, "%s", hashed_password.c_str()); sendLegacyInit(pName, pPassword); } if (CLIENT_PROTOCOL_VERSION_MAX >= 25) sendInit(myplayer->getName()); } // Not connected, return return; } /* Do stuff if connected */ /* Run Map's timers and unload unused data */ const float map_timer_and_unload_dtime = 5.25; if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) { ScopeProfiler sp(g_profiler, "Client: map timer and unload"); std::vector<v3s16> deleted_blocks; m_env.getMap().timerUpdate(map_timer_and_unload_dtime, g_settings->getFloat("client_unload_unused_data_timeout"), g_settings->getS32("client_mapblock_limit"), &deleted_blocks); /* Send info to server NOTE: This loop is intentionally iterated the way it is. */ std::vector<v3s16>::iterator i = deleted_blocks.begin(); std::vector<v3s16> sendlist; for(;;) { if(sendlist.size() == 255 || i == deleted_blocks.end()) { if(sendlist.empty()) break; /* [0] u16 command [2] u8 count [3] v3s16 pos_0 [3+6] v3s16 pos_1 ... */ sendDeletedBlocks(sendlist); if(i == deleted_blocks.end()) break; sendlist.clear(); } sendlist.push_back(*i); ++i; } } /* Handle environment */ // Control local player (0ms) LocalPlayer *player = m_env.getLocalPlayer(); assert(player != NULL); player->applyControl(dtime); // Step environment m_env.step(dtime); /* Get events */ for(;;) { ClientEnvEvent event = m_env.getClientEvent(); if(event.type == CEE_NONE) { break; } else if(event.type == CEE_PLAYER_DAMAGE) { if(m_ignore_damage_timer <= 0) { u8 damage = event.player_damage.amount; if(event.player_damage.send_to_server) sendDamage(damage); // Add to ClientEvent queue ClientEvent event; event.type = CE_PLAYER_DAMAGE; event.player_damage.amount = damage; m_client_event_queue.push(event); } } else if(event.type == CEE_PLAYER_BREATH) { u16 breath = event.player_breath.amount; sendBreath(breath); } } /* Print some info */ float &counter = m_avg_rtt_timer; counter += dtime; if(counter >= 10) { counter = 0.0; // connectedAndInitialized() is true, peer exists. float avg_rtt = getRTT(); infostream << "Client: avg_rtt=" << avg_rtt << std::endl; } /* Send player position to server */ { float &counter = m_playerpos_send_timer; counter += dtime; if((m_state == LC_Ready) && (counter >= m_recommended_send_interval)) { counter = 0.0; sendPlayerPos(); } } /* Replace updated meshes */ { int num_processed_meshes = 0; while (!m_mesh_update_thread.m_queue_out.empty()) { num_processed_meshes++; MinimapMapblock *minimap_mapblock = NULL; bool do_mapper_update = true; MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx(); MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p); if (block) { // Delete the old mesh if (block->mesh != NULL) { delete block->mesh; block->mesh = NULL; } if (r.mesh) { minimap_mapblock = r.mesh->moveMinimapMapblock(); if (minimap_mapblock == NULL) do_mapper_update = false; } if (r.mesh && r.mesh->getMesh()->getMeshBufferCount() == 0) { delete r.mesh; } else { // Replace with the new mesh block->mesh = r.mesh; } } else { delete r.mesh; } if (do_mapper_update) m_mapper->addBlock(r.p, minimap_mapblock); if (r.ack_block_to_server) { /* Acknowledge block [0] u8 count [1] v3s16 pos_0 */ sendGotBlocks(r.p); } } if (num_processed_meshes > 0) g_profiler->graphAdd("num_processed_meshes", num_processed_meshes); } /* Load fetched media */ if (m_media_downloader && m_media_downloader->isStarted()) { m_media_downloader->step(this); if (m_media_downloader->isDone()) { received_media(); delete m_media_downloader; m_media_downloader = NULL; } } /* If the server didn't update the inventory in a while, revert the local inventory (so the player notices the lag problem and knows something is wrong). */ if(m_inventory_from_server) { float interval = 10.0; float count_before = floor(m_inventory_from_server_age / interval); m_inventory_from_server_age += dtime; float count_after = floor(m_inventory_from_server_age / interval); if(count_after != count_before) { // Do this every <interval> seconds after TOCLIENT_INVENTORY // Reset the locally changed inventory to the authoritative inventory LocalPlayer *player = m_env.getLocalPlayer(); player->inventory = *m_inventory_from_server; m_inventory_updated = true; } } /* Update positions of sounds attached to objects */ { for(UNORDERED_MAP<int, u16>::iterator i = m_sounds_to_objects.begin(); i != m_sounds_to_objects.end(); ++i) { int client_id = i->first; u16 object_id = i->second; ClientActiveObject *cao = m_env.getActiveObject(object_id); if(!cao) continue; v3f pos = cao->getPosition(); m_sound->updateSoundPosition(client_id, pos); } } /* Handle removed remotely initiated sounds */ m_removed_sounds_check_timer += dtime; if(m_removed_sounds_check_timer >= 2.32) { m_removed_sounds_check_timer = 0; // Find removed sounds and clear references to them std::vector<s32> removed_server_ids; for(UNORDERED_MAP<s32, int>::iterator i = m_sounds_server_to_client.begin(); i != m_sounds_server_to_client.end();) { s32 server_id = i->first; int client_id = i->second; ++i; if(!m_sound->soundExists(client_id)) { m_sounds_server_to_client.erase(server_id); m_sounds_client_to_server.erase(client_id); m_sounds_to_objects.erase(client_id); removed_server_ids.push_back(server_id); } } // Sync to server if(!removed_server_ids.empty()) { sendRemovedSounds(removed_server_ids); } } // Write server map if (m_localdb && m_localdb_save_interval.step(dtime, m_cache_save_interval)) { m_localdb->endSave(); m_localdb->beginSave(); } }
void Server::handleCommand_Init_Legacy(NetworkPacket* pkt) { const auto peer_id = pkt->getPeerId(); auto & packet = *(pkt->packet); RemoteClient* client = getClient(pkt->getPeerId(), CS_Created); std::string addr_s; try { addr_s = getPeerAddress(pkt->getPeerId()).serializeString(); } catch (std::exception &e) { /* * no peer for this packet found * most common reason is peer timeout, e.g. peer didn't * respond for some time, your server was overloaded or * things like that. */ infostream << "Server::ProcessData(): Canceling: peer " << pkt->getPeerId() << " not found : " << e.what() << std::endl; return; } // If net_proto_version is set, this client has already been handled if(client->getState() > CS_Created) { verbosestream << "Server: Ignoring multiple TOSERVER_INITs from " << addr_s << " (peer_id=" << peer_id << ")" << std::endl; return; } verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id=" << peer_id << ")" << std::endl; // Do not allow multiple players in simple singleplayer mode. // This isn't a perfect way to do it, but will suffice for now if(m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1) { infostream << "Server: Not allowing another client (" << addr_s << ") to connect in simple singleplayer mode" << std::endl; DenyAccess(peer_id, "Running in simple singleplayer mode."); return; } // First byte after command is maximum supported // serialization version u8 client_max; packet[TOSERVER_INIT_LEGACY_FMT].convert(client_max); u8 our_max = SER_FMT_VER_HIGHEST_READ; // Use the highest version supported by both int deployed = std::min(client_max, our_max); // If it's lower than the lowest supported, give up. if (deployed < SER_FMT_VER_LOWEST_READ) deployed = SER_FMT_VER_INVALID; if (deployed == SER_FMT_VER_INVALID) { actionstream << "Server: A mismatched client tried to connect from " << addr_s << std::endl; infostream << "Server: Cannot negotiate serialization version with " << addr_s << std::endl; DenyAccess(peer_id, std::string( "Your client's version is not supported.\n" "Server version is ") + (g_version_string) + "." ); return; } client->setPendingSerializationVersion(deployed); /* Read and check network protocol version */ u16 min_net_proto_version = 0; packet[TOSERVER_INIT_LEGACY_PROTOCOL_VERSION_MIN].convert(min_net_proto_version); u16 max_net_proto_version = min_net_proto_version; packet[TOSERVER_INIT_LEGACY_PROTOCOL_VERSION_MAX].convert(max_net_proto_version); packet.convert_safe(TOSERVER_INIT_LEGACY_PROTOCOL_VERSION_FM, client->net_proto_version_fm); // Start with client's maximum version u16 net_proto_version = max_net_proto_version; // Figure out a working version if it is possible at all if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN || min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX) { // If maximum is larger than our maximum, go with our maximum if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX) net_proto_version = SERVER_PROTOCOL_VERSION_MAX; // Else go with client's maximum else net_proto_version = max_net_proto_version; } verbosestream << "Server: " << addr_s << ": Protocol version: min: " << min_net_proto_version << ", max: " << max_net_proto_version << ", chosen: " << net_proto_version << std::endl; client->net_proto_version = net_proto_version; if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN || net_proto_version > SERVER_PROTOCOL_VERSION_MAX) { actionstream << "Server: A mismatched client tried to connect from " << addr_s << std::endl; DenyAccess(peer_id, std::string( "Your client's version is not supported.\n" "Server version is ") + (g_version_string) + ",\n" + "server's PROTOCOL_VERSION is " + itos(SERVER_PROTOCOL_VERSION_MIN) + "..." + itos(SERVER_PROTOCOL_VERSION_MAX) + ", client's PROTOCOL_VERSION is " + itos(min_net_proto_version) + "..." + itos(max_net_proto_version) ); return; } if(g_settings->getBool("strict_protocol_version_checking")) { if(net_proto_version != LATEST_PROTOCOL_VERSION) { actionstream << "Server: A mismatched (strict) client tried to " << "connect from " << addr_s << std::endl; DenyAccess(peer_id, std::string( "Your client's version is not supported.\n" "Server version is ") + (g_version_string) + ",\n" + "server's PROTOCOL_VERSION (strict) is " + itos(LATEST_PROTOCOL_VERSION) + ", client's PROTOCOL_VERSION is " + itos(min_net_proto_version) + "..." + itos(max_net_proto_version) ); return; } } /* Set up player */ // Get player name std::string playername; packet[TOSERVER_INIT_LEGACY_NAME].convert(playername); if(playername.empty()) { actionstream << "Server: Player with an empty name " << "tried to connect from " << addr_s << std::endl; DenyAccess(peer_id, "Empty name"); return; } if(!g_settings->getBool("enable_any_name") && string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false) { actionstream << "Server: Player with an invalid name [" << playername << "] tried to connect from " << addr_s << std::endl; DenyAccess(peer_id, "Name contains unallowed characters"); return; } if(!isSingleplayer() && playername == "singleplayer") { actionstream << "Server: Player with the name \"singleplayer\" " << "tried to connect from " << addr_s << std::endl; DenyAccess(peer_id, "Name is not allowed"); return; } { std::string reason; if(m_script->on_prejoinplayer(playername, addr_s, &reason)) { actionstream << "Server: Player with the name \"" << playername << "\" " << "tried to connect from " << addr_s << " " << "but it was disallowed for the following reason: " << reason << std::endl; DenyAccess(peer_id, reason); return; } } infostream << "Server: New connection: \"" << playername << "\" from " << addr_s << " (peer_id=" << peer_id << ")" << std::endl; // Get password std::string given_password; packet[TOSERVER_INIT_LEGACY_PASSWORD].convert(given_password); if(!base64_is_valid(given_password.c_str())) { actionstream << "Server: " << playername << " supplied invalid password hash" << std::endl; DenyAccess(peer_id, "Invalid password hash"); return; } // Enforce user limit. // Don't enforce for users that have some admin right if(m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") && !checkPriv(playername, "server") && !checkPriv(playername, "ban") && !checkPriv(playername, "privs") && !checkPriv(playername, "password") && playername != g_settings->get("name")) { actionstream << "Server: " << playername << " tried to join, but there" << " are already max_users=" << g_settings->getU16("max_users") << " players." << std::endl; DenyAccess(peer_id, "Too many users."); return; } std::string checkpwd; // Password hash to check against bool has_auth = m_script->getAuth(playername, &checkpwd, NULL); // If no authentication info exists for user, create it if(!has_auth) { if(!isSingleplayer() && g_settings->getBool("disallow_empty_password") && given_password == "") { actionstream << "Server: " << playername << " supplied empty password" << std::endl; DenyAccess(peer_id, "Empty passwords are " "disallowed. Set a password and try again."); return; } std::string raw_default_password = g_settings->get("default_password"); std::string initial_password = translate_password(playername, raw_default_password); // If default_password is empty, allow any initial password if (raw_default_password.length() == 0) initial_password = given_password; m_script->createAuth(playername, initial_password); } has_auth = m_script->getAuth(playername, &checkpwd, NULL); if(!has_auth) { actionstream << "Server: " << playername << " cannot be authenticated" << " (auth handler does not work?)" << std::endl; DenyAccess(peer_id, "Not allowed to login"); return; } if(given_password != checkpwd) { actionstream << "Server: " << playername << " supplied wrong password" << " at " << addr_s << std::endl; DenyAccess(peer_id, "Wrong password"); return; } RemotePlayer *player = static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str())); if (player && player->peer_id != 0) { if (given_password.size()) { actionstream << "Server: " << playername << " rejoining" << std::endl; DenyAccessVerCompliant(player->peer_id, player->protocol_version, SERVER_ACCESSDENIED_ALREADY_CONNECTED); player->getPlayerSAO()->removingFromEnvironment(); m_env->removePlayer(player); player = nullptr; } else { errorstream << "Server: " << playername << ": Failed to emerge player" << " (player allocated to an another client)" << std::endl; DenyAccess(peer_id, "Another client is connected with this " "name. If your client closed unexpectedly, try again in " "a minute."); } } m_clients.setPlayerName(peer_id, playername); /* Answer with a TOCLIENT_INIT */ { MSGPACK_PACKET_INIT(TOCLIENT_INIT_LEGACY, 6); PACK(TOCLIENT_INIT_DEPLOYED, deployed); PACK(TOCLIENT_INIT_SEED, m_env->getServerMap().getSeed()); PACK(TOCLIENT_INIT_STEP, g_settings->getFloat("dedicated_server_step")); //if (player) //todo : remake me // PACK(TOCLIENT_INIT_POS, player->getPosition()); Settings params; m_emerge->params.save(params); PACK(TOCLIENT_INIT_MAP_PARAMS, params); PACK(TOCLIENT_INIT_PROTOCOL_VERSION_FM, SERVER_PROTOCOL_VERSION_FM); PACK(TOCLIENT_INIT_WEATHER, g_settings->getBool("weather")); // Send as reliable m_clients.send(peer_id, 0, buffer, true); m_clients.event(peer_id, CSE_InitLegacy); } return; }