From 9c53b4185118d61931faa753de9a20af7b22cd18 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 13 Jan 2019 19:32:30 +0000 Subject: [PATCH] Added PlayerInfo, Player is no longer accessible during PlayerPreLoginEvent --- src/pocketmine/Player.php | 34 ++++--- src/pocketmine/PlayerInfo.php | 97 +++++++++++++++++++ src/pocketmine/entity/Human.php | 4 +- .../event/player/PlayerPreLoginEvent.php | 51 ++++++++-- .../mcpe/handler/LoginSessionHandler.php | 4 +- .../network/mcpe/protocol/LoginPacket.php | 67 ++++++------- 6 files changed, 191 insertions(+), 66 deletions(-) create mode 100644 src/pocketmine/PlayerInfo.php diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 1ddd2db4b..b77400b5f 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -191,11 +191,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ /** @var NetworkSession */ protected $networkSession; - /** @var float */ - public $creationTime = 0; - /** @var bool */ - public $loggedIn = false; + protected $loggedIn = false; /** @var bool */ public $spawned = false; @@ -212,6 +209,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ protected $xuid = ""; /** @var bool */ protected $authenticated = false; + /** @var PlayerInfo|null */ + protected $playerInfo = null; protected $windowCnt = 2; /** @var int[] */ @@ -379,9 +378,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ * as SimpleAuth for authentication. This is NOT SAFE anymore as this UUID is now what was given by the client, NOT * a server-computed UUID.) * - * @return UUID|null + * @return UUID */ - public function getUniqueId() : ?UUID{ + public function getUniqueId() : UUID{ return parent::getUniqueId(); } @@ -693,8 +692,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->chunksPerTick = (int) $this->server->getProperty("chunk-sending.per-tick", 4); $this->spawnThreshold = (int) (($this->server->getProperty("chunk-sending.spawn-radius", 4) ** 2) * M_PI); - $this->creationTime = microtime(true); - $this->allowMovementCheats = (bool) $this->server->getProperty("player.anti-cheat.allow-movement-cheats", false); } @@ -1741,20 +1738,25 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } public function handleLogin(LoginPacket $packet) : bool{ - $this->username = TextFormat::clean($packet->username); + $this->playerInfo = $packet->playerInfo; + $this->username = TextFormat::clean($this->playerInfo->getUsername()); $this->displayName = $this->username; $this->iusername = strtolower($this->username); - $this->locale = $packet->locale; - $this->randomClientId = $packet->clientId; + $this->locale = $this->playerInfo->getLocale(); + $this->randomClientId = $this->playerInfo->getClientId(); - $this->uuid = UUID::fromString($packet->clientUUID); + $this->uuid = $this->playerInfo->getUuid(); $this->rawUUID = $this->uuid->toBinary(); - $this->xuid = $packet->xuid; + $this->xuid = $this->playerInfo->getXuid(); - $this->setSkin($packet->skin); + $this->setSkin($this->playerInfo->getSkin()); - - $ev = new PlayerPreLoginEvent($this, $this->server->requiresAuthentication()); + $ev = new PlayerPreLoginEvent( + $this->playerInfo, + $this->networkSession->getIp(), + $this->networkSession->getPort(), + $this->server->requiresAuthentication() + ); if(count($this->server->getOnlinePlayers()) >= $this->server->getMaxPlayers()){ $ev->setKickReason(PlayerPreLoginEvent::KICK_REASON_SERVER_FULL, "disconnectionScreen.serverFull"); } diff --git a/src/pocketmine/PlayerInfo.php b/src/pocketmine/PlayerInfo.php new file mode 100644 index 000000000..cc54f5529 --- /dev/null +++ b/src/pocketmine/PlayerInfo.php @@ -0,0 +1,97 @@ +username = $username; + $this->uuid = $uuid; + $this->skin = $skin; + $this->locale = $locale; + $this->xuid = $xuid; + $this->clientId = $clientId; + } + + /** + * @return string + */ + public function getUsername() : string{ + return $this->username; + } + + /** + * @return UUID + */ + public function getUuid() : UUID{ + return $this->uuid; + } + + /** + * @return Skin + */ + public function getSkin() : Skin{ + return $this->skin; + } + + /** + * @return string + */ + public function getLocale() : string{ + return $this->locale; + } + + /** + * @return string + */ + public function getXuid() : string{ + return $this->xuid; + } + + /** + * @return int + */ + public function getClientId() : int{ + return $this->clientId; + } +} diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index 0ab10733d..a7b379471 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -129,9 +129,9 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ } /** - * @return UUID|null + * @return UUID */ - public function getUniqueId() : ?UUID{ + public function getUniqueId() : UUID{ return $this->uuid; } diff --git a/src/pocketmine/event/player/PlayerPreLoginEvent.php b/src/pocketmine/event/player/PlayerPreLoginEvent.php index 18aa5f081..fef54b7f9 100644 --- a/src/pocketmine/event/player/PlayerPreLoginEvent.php +++ b/src/pocketmine/event/player/PlayerPreLoginEvent.php @@ -23,7 +23,8 @@ declare(strict_types=1); namespace pocketmine\event\player; -use pocketmine\Player; +use pocketmine\event\Event; +use pocketmine\PlayerInfo; use function array_keys; /** @@ -35,11 +36,8 @@ use function array_keys; * * WARNING: Any information about the player CANNOT be trusted at this stage, because they are not authenticated and * could be a hacker posing as another player. - * - * WARNING: Due to internal bad architecture, the player is not fully constructed at this stage, and errors might occur - * when calling API methods on the player. Tread with caution. */ -class PlayerPreLoginEvent extends PlayerEvent{ +class PlayerPreLoginEvent extends Event{ public const KICK_REASON_PLUGIN = 0; public const KICK_REASON_SERVER_FULL = 1; public const KICK_REASON_SERVER_WHITELISTED = 2; @@ -52,6 +50,12 @@ class PlayerPreLoginEvent extends PlayerEvent{ self::KICK_REASON_BANNED ]; + /** @var PlayerInfo */ + private $playerInfo; + /** @var string */ + private $ip; + /** @var int */ + private $port; /** @var bool */ protected $authRequired; @@ -59,14 +63,43 @@ class PlayerPreLoginEvent extends PlayerEvent{ protected $kickReasons = []; /** - * @param Player $player - * @param bool $authRequired + * @param PlayerInfo $playerInfo + * @param string $ip + * @param int $port + * @param bool $authRequired */ - public function __construct(Player $player, bool $authRequired){ - $this->player = $player; + public function __construct(PlayerInfo $playerInfo, string $ip, int $port, bool $authRequired){ + $this->playerInfo = $playerInfo; + $this->ip = $ip; + $this->port = $port; $this->authRequired = $authRequired; } + /** + * Returns an object containing self-proclaimed information about the connecting player. + * WARNING: THE PLAYER IS NOT VERIFIED DURING THIS EVENT. At this point, it's unknown if the player is real or a + * hacker. + * + * @return PlayerInfo + */ + public function getPlayerInfo() : PlayerInfo{ + return $this->playerInfo; + } + + /** + * @return string + */ + public function getIp() : string{ + return $this->ip; + } + + /** + * @return int + */ + public function getPort() : int{ + return $this->port; + } + /** * @return bool */ diff --git a/src/pocketmine/network/mcpe/handler/LoginSessionHandler.php b/src/pocketmine/network/mcpe/handler/LoginSessionHandler.php index 6fd4d9a56..7db856541 100644 --- a/src/pocketmine/network/mcpe/handler/LoginSessionHandler.php +++ b/src/pocketmine/network/mcpe/handler/LoginSessionHandler.php @@ -60,13 +60,13 @@ class LoginSessionHandler extends SessionHandler{ return true; } - if(!Player::isValidUserName($packet->username)){ + if(!Player::isValidUserName($packet->playerInfo->getUsername())){ $this->session->disconnect("disconnectionScreen.invalidName"); return true; } - if($packet->skin === null or !$packet->skin->isValid()){ + if(!$packet->playerInfo->getSkin()->isValid()){ $this->session->disconnect("disconnectionScreen.invalidSkin"); return true; diff --git a/src/pocketmine/network/mcpe/protocol/LoginPacket.php b/src/pocketmine/network/mcpe/protocol/LoginPacket.php index bad35b06b..99d16930d 100644 --- a/src/pocketmine/network/mcpe/protocol/LoginPacket.php +++ b/src/pocketmine/network/mcpe/protocol/LoginPacket.php @@ -29,8 +29,10 @@ namespace pocketmine\network\mcpe\protocol; use Particle\Validator\Validator; use pocketmine\entity\Skin; use pocketmine\network\mcpe\handler\SessionHandler; +use pocketmine\PlayerInfo; use pocketmine\utils\BinaryStream; use pocketmine\utils\Utils; +use pocketmine\utils\UUID; use function array_filter; use function base64_decode; use function count; @@ -44,6 +46,10 @@ class LoginPacket extends DataPacket{ public const EDITION_POCKET = 0; + public const I_USERNAME = 'displayName'; + public const I_UUID = 'identity'; + public const I_XUID = 'XUID'; + public const I_CLIENT_RANDOM_ID = 'ClientRandomId'; public const I_SERVER_ADDRESS = 'ServerAddress'; public const I_LANGUAGE_CODE = 'LanguageCode'; @@ -54,27 +60,16 @@ class LoginPacket extends DataPacket{ public const I_GEOMETRY_NAME = 'SkinGeometryName'; public const I_GEOMETRY_DATA = 'SkinGeometry'; - /** @var string */ - public $username; /** @var int */ public $protocol; - /** @var string */ - public $clientUUID; - /** @var int */ - public $clientId; - /** @var string */ - public $xuid; - /** @var string */ - public $identityPublicKey; - /** @var string */ - public $serverAddress; - /** @var string */ - public $locale; - /** @var Skin|null */ - public $skin; + + /** @var PlayerInfo */ + public $playerInfo; /** @var string[] array of encoded JWT */ public $chainDataJwt = []; + /** @var array|null extraData index of whichever JWT has it */ + public $extraData = null; /** @var string */ public $clientDataJwt; /** @var array decoded payload of the clientData JWT */ @@ -138,8 +133,6 @@ class LoginPacket extends DataPacket{ self::validate($vd, "chainData", $chainData); $this->chainDataJwt = $chainData['chain']; - - $hasExtraData = false; foreach($this->chainDataJwt as $k => $chain){ //validate every chain element $claims = Utils::getJwtClaims($chain); @@ -147,23 +140,20 @@ class LoginPacket extends DataPacket{ if(!is_array($claims["extraData"])){ throw new \UnexpectedValueException("'extraData' key should be an array"); } - if($hasExtraData){ + if($this->extraData !== null){ throw new \UnexpectedValueException("Found 'extraData' more than once in chainData"); } - $hasExtraData = true; $extraV = new Validator(); - $extraV->required('displayName')->string(); - $extraV->required('identity')->uuid(); - $extraV->required('XUID')->string()->digits()->allowEmpty(true); + $extraV->required(self::I_USERNAME)->string(); + $extraV->required(self::I_UUID)->uuid(); + $extraV->required(self::I_XUID)->string()->digits()->allowEmpty(true); self::validate($extraV, "chain.$k.extraData", $claims['extraData']); - $this->username = $claims["extraData"]["displayName"]; - $this->clientUUID = $claims["extraData"]["identity"]; - $this->xuid = $claims["extraData"]["XUID"]; + $this->extraData = $claims['extraData']; } } - if(!$hasExtraData){ + if($this->extraData === null){ throw new \UnexpectedValueException("'extraData' not found in chain data"); } @@ -184,16 +174,19 @@ class LoginPacket extends DataPacket{ $this->clientData = $clientData; - $this->clientId = $this->clientData[self::I_CLIENT_RANDOM_ID]; - $this->serverAddress = $this->clientData[self::I_SERVER_ADDRESS]; - $this->locale = $this->clientData[self::I_LANGUAGE_CODE]; - - $this->skin = new Skin( - $this->clientData[self::I_SKIN_ID], - base64_decode($this->clientData[self::I_SKIN_DATA]), - base64_decode($this->clientData[self::I_CAPE_DATA]), - $this->clientData[self::I_GEOMETRY_NAME], - base64_decode($this->clientData[self::I_GEOMETRY_DATA]) + $this->playerInfo = new PlayerInfo( + $this->extraData[self::I_USERNAME], + UUID::fromString($this->extraData[self::I_UUID]), + new Skin( + $this->clientData[self::I_SKIN_ID], + base64_decode($this->clientData[self::I_SKIN_DATA]), + base64_decode($this->clientData[self::I_CAPE_DATA]), + $this->clientData[self::I_GEOMETRY_NAME], + base64_decode($this->clientData[self::I_GEOMETRY_DATA]) + ), + $this->clientData[self::I_LANGUAGE_CODE], + $this->extraData[self::I_XUID], + $this->clientData[self::I_CLIENT_RANDOM_ID] ); }