diff --git a/src/ClientTlsContext.php b/src/ClientTlsContext.php index 7dc6182..6724542 100644 --- a/src/ClientTlsContext.php +++ b/src/ClientTlsContext.php @@ -22,6 +22,8 @@ final class ClientTlsContext private bool $verifyPeer = true; + private bool $verifyPeerName = true; + private int $verifyDepth = 10; private ?array $peerFingerprint = null; @@ -127,6 +129,8 @@ public function withoutPeerVerification(): self { $clone = clone $this; $clone->verifyPeer = false; + // This is for compatibility with the former behaviour: + $clone->verifyPeerName = false; return $clone; } @@ -139,6 +143,40 @@ public function hasPeerVerification(): bool return $this->verifyPeer; } + /** + * Enable peer name verification, this is the default with verifyPeer enabled. + * + * @return self Cloned, modified instance. + */ + public function withPeerNameVerification(): self + { + $clone = clone $this; + $clone->verifyPeerName = true; + + return $clone; + } + + /** + * Disable peer name verification. + * + * @return self Cloned, modified instance. + */ + public function withoutPeerNameVerification(): self + { + $clone = clone $this; + $clone->verifyPeerName = false; + + return $clone; + } + + /** + * @return bool Whether peer verification is enabled. + */ + public function hasPeerNameVerification(): bool + { + return $this->verifyPeerName; + } + /** * Maximum chain length the peer might present including the certificates in the local trust store. * @@ -452,7 +490,7 @@ public function toStreamContextArray(): array 'crypto_method' => $this->toStreamCryptoMethod(), 'peer_name' => $this->peerName, 'verify_peer' => $this->verifyPeer, - 'verify_peer_name' => $this->verifyPeer, + 'verify_peer_name' => $this->verifyPeerName, 'verify_depth' => $this->verifyDepth, 'ciphers' => $this->ciphers ?? \OPENSSL_DEFAULT_STREAM_CIPHERS, 'capture_peer_cert' => $this->capturePeer, diff --git a/src/ServerTlsContext.php b/src/ServerTlsContext.php index 46da393..eeec1b7 100644 --- a/src/ServerTlsContext.php +++ b/src/ServerTlsContext.php @@ -58,6 +58,8 @@ public static function fromServerResource($socket): ?self private bool $verifyPeer = false; + private bool $verifyPeerName = true; + private int $verifyDepth = 10; private ?string $ciphers = null; @@ -166,6 +168,40 @@ public function hasPeerVerification(): bool return $this->verifyPeer; } + /** + * Enable peer name verification, this is the default with verifyPeer enabled. + * + * @return self Cloned, modified instance. + */ + public function withPeerNameVerification(): self + { + $clone = clone $this; + $clone->verifyPeerName = true; + + return $clone; + } + + /** + * Disable peer name verification. + * + * @return self Cloned, modified instance. + */ + public function withoutPeerNameVerification(): self + { + $clone = clone $this; + $clone->verifyPeerName = false; + + return $clone; + } + + /** + * @return bool Whether peer verification is enabled. + */ + public function hasPeerNameVerification(): bool + { + return $this->verifyPeer && $this->verifyPeerName; + } + /** * Maximum chain length the peer might present including the certificates in the local trust store. * @@ -437,7 +473,7 @@ public function toStreamContextArray(): array 'crypto_method' => $this->toStreamCryptoMethod(), 'peer_name' => $this->peerName, 'verify_peer' => $this->verifyPeer, - 'verify_peer_name' => $this->verifyPeer, + 'verify_peer_name' => $this->verifyPeer && $this->verifyPeerName, 'verify_depth' => $this->verifyDepth, 'ciphers' => $this->ciphers ?? \OPENSSL_DEFAULT_STREAM_CIPHERS, 'honor_cipher_order' => true, diff --git a/test/ClientTlsContextTest.php b/test/ClientTlsContextTest.php index be70b34..258dca6 100644 --- a/test/ClientTlsContextTest.php +++ b/test/ClientTlsContextTest.php @@ -85,6 +85,31 @@ public function testWithoutPeerVerification(): void self::assertFalse($clonedContext->hasPeerVerification()); } + public function testDefaultPeerNameVerification(): void + { + $context = new ClientTlsContext; + self::assertTrue($context->hasPeerNameVerification()); + } + + public function testWithoutPeerNameVerification(): void + { + $context = new ClientTlsContext; + $clonedContext = $context->withPeerVerification()->withoutPeerNameVerification(); + + self::assertTrue($clonedContext->hasPeerVerification()); + self::assertFalse($clonedContext->hasPeerNameVerification()); + } + + public function testContextOptionsWithoutPeerNameVerification(): void + { + $context = new ClientTlsContext; + $clonedContext = $context->withPeerVerification()->withoutPeerNameVerification(); + $options = $clonedContext->toStreamContextArray()['ssl']; + + self::assertTrue($options['verify_peer']); + self::assertFalse($options['verify_peer_name']); + } + public function certificateDataProvider(): array { return [ diff --git a/test/ServerTlsContextTest.php b/test/ServerTlsContextTest.php index 0d74413..7b6d9d3 100644 --- a/test/ServerTlsContextTest.php +++ b/test/ServerTlsContextTest.php @@ -85,6 +85,31 @@ public function testWithoutPeerVerification(): void self::assertFalse($clonedContext->hasPeerVerification()); } + public function testDefaultPeerNameVerification(): void + { + $context = new ServerTlsContext; + self::assertFalse($context->hasPeerNameVerification()); + } + + public function testWithoutPeerNameVerification(): void + { + $context = new ServerTlsContext; + $clonedContext = $context->withPeerVerification()->withoutPeerNameVerification(); + + self::assertTrue($clonedContext->hasPeerVerification()); + self::assertFalse($clonedContext->hasPeerNameVerification()); + } + + public function testContextOptionsWithoutPeerNameVerification(): void + { + $context = new ServerTlsContext; + $clonedContext = $context->withPeerVerification()->withoutPeerNameVerification(); + $options = $clonedContext->toStreamContextArray()['ssl']; + + self::assertTrue($options['verify_peer']); + self::assertFalse($options['verify_peer_name']); + } + public function verifyDepthDataProvider(): array { return [