mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	network: check Console ID conflicts
As Console ID can be sensitive data sometimes, this implementation sent a SHA256 hash of it instead.
This commit is contained in:
		
							parent
							
								
									3c589f473f
								
							
						
					
					
						commit
						c396e3c6e5
					
				
					 14 changed files with 109 additions and 23 deletions
				
			
		|  | @ -39,6 +39,7 @@ | |||
| #include "core/frontend/applets/default_applets.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/hle/service/am/am.h" | ||||
| #include "core/hle/service/cfg/cfg.h" | ||||
| #include "core/loader/loader.h" | ||||
| #include "core/movie.h" | ||||
| #include "core/settings.h" | ||||
|  | @ -336,7 +337,8 @@ int main(int argc, char** argv) { | |||
|             member->BindOnStateChanged(OnStateChanged); | ||||
|             LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, | ||||
|                       nickname); | ||||
|             member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredMac, password); | ||||
|             member->Join(nickname, Service::CFG::GetConsoleIdHash(system), address.c_str(), port, 0, | ||||
|                          Network::NoPreferredMac, password); | ||||
|         } else { | ||||
|             LOG_ERROR(Network, "Could not access RoomMember"); | ||||
|             return 0; | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ | |||
| #include "citra_qt/multiplayer/state.h" | ||||
| #include "citra_qt/multiplayer/validation.h" | ||||
| #include "citra_qt/ui_settings.h" | ||||
| #include "core/hle/service/cfg/cfg.h" | ||||
| #include "core/settings.h" | ||||
| #include "network/network.h" | ||||
| #include "ui_direct_connect.h" | ||||
|  | @ -97,6 +98,7 @@ void DirectConnectWindow::Connect() { | |||
|         if (auto room_member = Network::GetRoomMember().lock()) { | ||||
|             auto port = UISettings::values.port.toUInt(); | ||||
|             room_member->Join(ui->nickname->text().toStdString(), | ||||
|                               Service::CFG::GetConsoleIdHash(Core::System::GetInstance()), | ||||
|                               ui->ip->text().toStdString().c_str(), port, 0, | ||||
|                               Network::NoPreferredMac, ui->password->text().toStdString().c_str()); | ||||
|         } | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ | |||
| #include "citra_qt/ui_settings.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/announce_multiplayer_session.h" | ||||
| #include "core/hle/service/cfg/cfg.h" | ||||
| #include "core/settings.h" | ||||
| #include "ui_host_room.h" | ||||
| 
 | ||||
|  | @ -116,8 +117,9 @@ void HostRoomWindow::Host() { | |||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         member->Join(ui->username->text().toStdString(), "127.0.0.1", port, 0, | ||||
|                      Network::NoPreferredMac, password); | ||||
|         member->Join(ui->username->text().toStdString(), | ||||
|                      Service::CFG::GetConsoleIdHash(Core::System::GetInstance()), "127.0.0.1", port, | ||||
|                      0, Network::NoPreferredMac, password); | ||||
| 
 | ||||
|         // Store settings
 | ||||
|         UISettings::values.room_nickname = ui->username->text(); | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ | |||
| #include "citra_qt/multiplayer/validation.h" | ||||
| #include "citra_qt/ui_settings.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/hle/service/cfg/cfg.h" | ||||
| #include "core/settings.h" | ||||
| #include "network/network.h" | ||||
| 
 | ||||
|  | @ -139,7 +140,8 @@ void Lobby::OnJoinRoom(const QModelIndex& source) { | |||
|     // attempt to connect in a different thread
 | ||||
|     QFuture<void> f = QtConcurrent::run([nickname, ip, port, password] { | ||||
|         if (auto room_member = Network::GetRoomMember().lock()) { | ||||
|             room_member->Join(nickname, ip.c_str(), port, 0, Network::NoPreferredMac, password); | ||||
|             room_member->Join(nickname, Service::CFG::GetConsoleIdHash(Core::System::GetInstance()), | ||||
|                               ip.c_str(), port, 0, Network::NoPreferredMac, password); | ||||
|         } | ||||
|     }); | ||||
|     watcher->setFuture(f); | ||||
|  |  | |||
|  | @ -38,6 +38,9 @@ const ConnectionError GENERIC_ERROR( | |||
| const ConnectionError LOST_CONNECTION(QT_TR_NOOP("Connection to room lost. Try to reconnect.")); | ||||
| const ConnectionError MAC_COLLISION( | ||||
|     QT_TR_NOOP("MAC address is already in use. Please choose another.")); | ||||
| const ConnectionError CONSOLE_ID_COLLISION(QT_TR_NOOP( | ||||
|     "Your Console ID conflicted with someone else's in the room.\n\nPlease go to Emulation " | ||||
|     "> Configure > System to regenerate your Console ID.")); | ||||
| 
 | ||||
| static bool WarnMessage(const std::string& title, const std::string& text) { | ||||
|     return QMessageBox::Ok == QMessageBox::warning(nullptr, QObject::tr(title.c_str()), | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ extern const ConnectionError WRONG_PASSWORD; | |||
| extern const ConnectionError GENERIC_ERROR; | ||||
| extern const ConnectionError LOST_CONNECTION; | ||||
| extern const ConnectionError MAC_COLLISION; | ||||
| extern const ConnectionError CONSOLE_ID_COLLISION; | ||||
| 
 | ||||
| /**
 | ||||
|  *  Shows a standard QMessageBox with a error message | ||||
|  |  | |||
|  | @ -102,6 +102,9 @@ void MultiplayerState::OnNetworkStateChanged(const Network::RoomMember::State& s | |||
|     case Network::RoomMember::State::MacCollision: | ||||
|         NetworkMessage::ShowError(NetworkMessage::MAC_COLLISION); | ||||
|         break; | ||||
|     case Network::RoomMember::State::ConsoleIdCollision: | ||||
|         NetworkMessage::ShowError(NetworkMessage::CONSOLE_ID_COLLISION); | ||||
|         break; | ||||
|     case Network::RoomMember::State::RoomIsFull: | ||||
|         NetworkMessage::ShowError(NetworkMessage::ROOM_IS_FULL); | ||||
|         break; | ||||
|  |  | |||
|  | @ -734,4 +734,21 @@ void InstallInterfaces(Core::System& system) { | |||
|     std::make_shared<CFG_NOR>()->InstallAsService(service_manager); | ||||
| } | ||||
| 
 | ||||
| std::string GetConsoleIdHash(Core::System& system) { | ||||
|     u64_le console_id{}; | ||||
|     std::array<u8, sizeof(console_id)> buffer; | ||||
|     if (system.IsPoweredOn()) { | ||||
|         auto cfg = GetModule(system); | ||||
|         ASSERT_MSG(cfg, "CFG Module missing!"); | ||||
|         console_id = cfg->GetConsoleUniqueId(); | ||||
|     } else { | ||||
|         console_id = std::make_unique<Service::CFG::Module>()->GetConsoleUniqueId(); | ||||
|     } | ||||
|     std::memcpy(buffer.data(), &console_id, sizeof(console_id)); | ||||
| 
 | ||||
|     std::array<u8, CryptoPP::SHA256::DIGESTSIZE> hash; | ||||
|     CryptoPP::SHA256().CalculateDigest(hash.data(), buffer.data(), sizeof(buffer)); | ||||
|     return fmt::format("{:02x}", fmt::join(hash.begin(), hash.end(), "")); | ||||
| } | ||||
| 
 | ||||
| } // namespace Service::CFG
 | ||||
|  |  | |||
|  | @ -415,4 +415,7 @@ std::shared_ptr<Module> GetModule(Core::System& system); | |||
| 
 | ||||
| void InstallInterfaces(Core::System& system); | ||||
| 
 | ||||
| /// Convenience function for getting a SHA256 hash of the Console ID
 | ||||
| std::string GetConsoleIdHash(Core::System& system); | ||||
| 
 | ||||
| } // namespace Service::CFG
 | ||||
|  |  | |||
|  | @ -31,10 +31,11 @@ public: | |||
|     std::string password; ///< The password required to connect to this room.
 | ||||
| 
 | ||||
|     struct Member { | ||||
|         std::string nickname;   ///< The nickname of the member.
 | ||||
|         GameInfo game_info;     ///< The current game of the member
 | ||||
|         MacAddress mac_address; ///< The assigned mac address of the member.
 | ||||
|         ENetPeer* peer;         ///< The remote peer.
 | ||||
|         std::string nickname;        ///< The nickname of the member.
 | ||||
|         std::string console_id_hash; ///< A hash of the console ID of the member.
 | ||||
|         GameInfo game_info;          ///< The current game of the member
 | ||||
|         MacAddress mac_address;      ///< The assigned mac address of the member.
 | ||||
|         ENetPeer* peer;              ///< The remote peer.
 | ||||
|     }; | ||||
|     using MemberList = std::vector<Member>; | ||||
|     MemberList members;              ///< Information about the members of this room
 | ||||
|  | @ -69,6 +70,12 @@ public: | |||
|      */ | ||||
|     bool IsValidMacAddress(const MacAddress& address) const; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Returns whether the console ID (hash) is valid, ie. isn't already taken by someone else in | ||||
|      * the room. | ||||
|      */ | ||||
|     bool IsValidConsoleId(const std::string& console_id_hash) const; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Sends a ID_ROOM_IS_FULL message telling the client that the room is full. | ||||
|      */ | ||||
|  | @ -84,6 +91,12 @@ public: | |||
|      */ | ||||
|     void SendMacCollision(ENetPeer* client); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Sends a IdConsoleIdCollison message telling the client that another member with the same | ||||
|      * console ID exists. | ||||
|      */ | ||||
|     void SendConsoleIdCollision(ENetPeer* client); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the version is invalid. | ||||
|      */ | ||||
|  | @ -212,6 +225,9 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { | |||
|     std::string nickname; | ||||
|     packet >> nickname; | ||||
| 
 | ||||
|     std::string console_id_hash; | ||||
|     packet >> console_id_hash; | ||||
| 
 | ||||
|     MacAddress preferred_mac; | ||||
|     packet >> preferred_mac; | ||||
| 
 | ||||
|  | @ -242,6 +258,11 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { | |||
|         preferred_mac = GenerateMacAddress(); | ||||
|     } | ||||
| 
 | ||||
|     if (!IsValidConsoleId(console_id_hash)) { | ||||
|         SendConsoleIdCollision(event->peer); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (client_version != network_version) { | ||||
|         SendVersionMismatch(event->peer); | ||||
|         return; | ||||
|  | @ -250,6 +271,7 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { | |||
|     // At this point the client is ready to be added to the room.
 | ||||
|     Member member{}; | ||||
|     member.mac_address = preferred_mac; | ||||
|     member.console_id_hash = console_id_hash; | ||||
|     member.nickname = nickname; | ||||
|     member.peer = event->peer; | ||||
| 
 | ||||
|  | @ -282,6 +304,14 @@ bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const { | |||
|                        [&address](const auto& member) { return member.mac_address != address; }); | ||||
| } | ||||
| 
 | ||||
| bool Room::RoomImpl::IsValidConsoleId(const std::string& console_id_hash) const { | ||||
|     // A Console ID is valid if it is not already taken by anybody else in the room.
 | ||||
|     std::lock_guard<std::mutex> lock(member_mutex); | ||||
|     return std::all_of(members.begin(), members.end(), [&console_id_hash](const auto& member) { | ||||
|         return member.console_id_hash != console_id_hash; | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| void Room::RoomImpl::SendNameCollision(ENetPeer* client) { | ||||
|     Packet packet; | ||||
|     packet << static_cast<u8>(IdNameCollision); | ||||
|  | @ -302,6 +332,16 @@ void Room::RoomImpl::SendMacCollision(ENetPeer* client) { | |||
|     enet_host_flush(server); | ||||
| } | ||||
| 
 | ||||
| void Room::RoomImpl::SendConsoleIdCollision(ENetPeer* client) { | ||||
|     Packet packet; | ||||
|     packet << static_cast<u8>(IdConsoleIdCollision); | ||||
| 
 | ||||
|     ENetPacket* enet_packet = | ||||
|         enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); | ||||
|     enet_peer_send(client, 0, enet_packet); | ||||
|     enet_host_flush(server); | ||||
| } | ||||
| 
 | ||||
| void Room::RoomImpl::SendWrongPassword(ENetPeer* client) { | ||||
|     Packet packet; | ||||
|     packet << static_cast<u8>(IdWrongPassword); | ||||
|  |  | |||
|  | @ -59,6 +59,7 @@ enum RoomMessageTypes : u8 { | |||
|     IdWrongPassword, | ||||
|     IdCloseRoom, | ||||
|     IdRoomIsFull, | ||||
|     IdConsoleIdCollision, | ||||
| }; | ||||
| 
 | ||||
| /// This is what a server [person creating a server] would use.
 | ||||
|  |  | |||
|  | @ -73,11 +73,12 @@ public: | |||
|      * Sends a request to the server, asking for permission to join a room with the specified | ||||
|      * nickname and preferred mac. | ||||
|      * @params nickname The desired nickname. | ||||
|      * @params console_id_hash A hash of the Console ID. | ||||
|      * @params preferred_mac The preferred MAC address to use in the room, the NoPreferredMac tells | ||||
|      * @params password The password for the room | ||||
|      * the server to assign one for us. | ||||
|      */ | ||||
|     void SendJoinRequest(const std::string& nickname, | ||||
|     void SendJoinRequest(const std::string& nickname, const std::string& console_id_hash, | ||||
|                          const MacAddress& preferred_mac = NoPreferredMac, | ||||
|                          const std::string& password = ""); | ||||
| 
 | ||||
|  | @ -163,6 +164,9 @@ void RoomMember::RoomMemberImpl::MemberLoop() { | |||
|                 case IdMacCollision: | ||||
|                     SetState(State::MacCollision); | ||||
|                     break; | ||||
|                 case IdConsoleIdCollision: | ||||
|                     SetState(State::ConsoleIdCollision); | ||||
|                     break; | ||||
|                 case IdVersionMismatch: | ||||
|                     SetState(State::WrongVersion); | ||||
|                     break; | ||||
|  | @ -204,11 +208,13 @@ void RoomMember::RoomMemberImpl::Send(Packet&& packet) { | |||
| } | ||||
| 
 | ||||
| void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname, | ||||
|                                                  const std::string& console_id_hash, | ||||
|                                                  const MacAddress& preferred_mac, | ||||
|                                                  const std::string& password) { | ||||
|     Packet packet; | ||||
|     packet << static_cast<u8>(IdJoinRequest); | ||||
|     packet << nickname; | ||||
|     packet << console_id_hash; | ||||
|     packet << preferred_mac; | ||||
|     packet << network_version; | ||||
|     packet << password; | ||||
|  | @ -392,9 +398,9 @@ RoomInformation RoomMember::GetRoomInformation() const { | |||
|     return room_member_impl->room_information; | ||||
| } | ||||
| 
 | ||||
| void RoomMember::Join(const std::string& nick, const char* server_addr, u16 server_port, | ||||
|                       u16 client_port, const MacAddress& preferred_mac, | ||||
|                       const std::string& password) { | ||||
| void RoomMember::Join(const std::string& nick, const std::string& console_id_hash, | ||||
|                       const char* server_addr, u16 server_port, u16 client_port, | ||||
|                       const MacAddress& preferred_mac, const std::string& password) { | ||||
|     // If the member is connected, kill the connection first
 | ||||
|     if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) { | ||||
|         Leave(); | ||||
|  | @ -427,7 +433,7 @@ void RoomMember::Join(const std::string& nick, const char* server_addr, u16 serv | |||
|     if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { | ||||
|         room_member_impl->nickname = nick; | ||||
|         room_member_impl->StartLoop(); | ||||
|         room_member_impl->SendJoinRequest(nick, preferred_mac, password); | ||||
|         room_member_impl->SendJoinRequest(nick, console_id_hash, preferred_mac, password); | ||||
|         SendGameInfo(room_member_impl->current_game_info); | ||||
|     } else { | ||||
|         enet_peer_disconnect(room_member_impl->server, 0); | ||||
|  |  | |||
|  | @ -54,12 +54,13 @@ public: | |||
|         LostConnection, ///< Connection closed
 | ||||
| 
 | ||||
|         // Reasons why connection was rejected
 | ||||
|         NameCollision,   ///< Somebody is already using this name
 | ||||
|         MacCollision,    ///< Somebody is already using that mac-address
 | ||||
|         WrongVersion,    ///< The room version is not the same as for this RoomMember
 | ||||
|         WrongPassword,   ///< The password doesn't match the one from the Room
 | ||||
|         CouldNotConnect, ///< The room is not responding to a connection attempt
 | ||||
|         RoomIsFull       ///< Room is already at the maximum number of players
 | ||||
|         NameCollision,      ///< Somebody is already using this name
 | ||||
|         MacCollision,       ///< Somebody is already using that mac-address
 | ||||
|         ConsoleIdCollision, ///< Somebody in the room has the same Console ID
 | ||||
|         WrongVersion,       ///< The room version is not the same as for this RoomMember
 | ||||
|         WrongPassword,      ///< The password doesn't match the one from the Room
 | ||||
|         CouldNotConnect,    ///< The room is not responding to a connection attempt
 | ||||
|         RoomIsFull          ///< Room is already at the maximum number of players
 | ||||
|     }; | ||||
| 
 | ||||
|     struct MemberInformation { | ||||
|  | @ -116,11 +117,13 @@ public: | |||
| 
 | ||||
|     /**
 | ||||
|      * Attempts to join a room at the specified address and port, using the specified nickname. | ||||
|      * This may fail if the username is already taken. | ||||
|      * A console ID hash is passed in to check console ID conflicts. | ||||
|      * This may fail if the username or console ID is already taken. | ||||
|      */ | ||||
|     void Join(const std::string& nickname, const char* server_addr = "127.0.0.1", | ||||
|               const u16 server_port = DefaultRoomPort, const u16 client_port = 0, | ||||
|               const MacAddress& preferred_mac = NoPreferredMac, const std::string& password = ""); | ||||
|     void Join(const std::string& nickname, const std::string& console_id_hash, | ||||
|               const char* server_addr = "127.0.0.1", const u16 server_port = DefaultRoomPort, | ||||
|               const u16 client_port = 0, const MacAddress& preferred_mac = NoPreferredMac, | ||||
|               const std::string& password = ""); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Sends a WiFi packet to the room. | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue