Skip to content

Commit

Permalink
Merge branch pull request #7496 into 2.7
Browse files Browse the repository at this point in the history
Adds ssl_ context options to SmtpTransport via CakeSocket. Both
CakeSocket and SmtpTransport can now use all the SSL context options
with the same syntax as HttpSocket.
  • Loading branch information
markstory committed Oct 13, 2015
2 parents 29490eb + 3a4facb commit 94aeee4
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 88 deletions.
43 changes: 41 additions & 2 deletions lib/Cake/Network/CakeSocket.php
Expand Up @@ -134,6 +134,7 @@ public function connect() {
$scheme = $this->config['protocol'] . '://';
}

$this->_setSslContext($this->config['host']);
if (!empty($this->config['context'])) {
$context = stream_context_create($this->config['context']);
} else {
Expand Down Expand Up @@ -195,6 +196,46 @@ public function connect() {
return $this->connected;
}

/**
* Configure the SSL context options.
*
* @param string $host The host name being connected to.
*/
protected function _setSslContext($host)
{
foreach ($this->config as $key => $value) {
if (substr($key, 0, 4) !== 'ssl_') {
continue;
}
$contextKey = substr($key, 4);
if (empty($this->config['context']['ssl'][$contextKey])) {
$this->config['context']['ssl'][$contextKey] = $value;
}
unset($this->config[$key]);
}
if (version_compare(PHP_VERSION, '5.3.2', '>=')) {
if (!isset($this->config['context']['ssl']['SNI_enabled'])) {
$this->config['context']['ssl']['SNI_enabled'] = true;
}
if (version_compare(PHP_VERSION, '5.6.0', '>=')) {
if (empty($this->config['context']['ssl']['peer_name'])) {
$this->config['context']['ssl']['peer_name'] = $host;
}
} else {
if (empty($this->config['context']['ssl']['SNI_server_name'])) {
$this->config['context']['ssl']['SNI_server_name'] = $host;
}
}
}
if (empty($this->config['context']['ssl']['cafile'])) {
$this->config['context']['ssl']['cafile'] = CAKE . 'Config' . DS . 'cacert.pem';
}
if (!empty($this->config['context']['ssl']['verify_host'])) {
$this->config['context']['ssl']['CN_match'] = $host;
}
unset($this->config['context']['ssl']['verify_host']);
}

/**
* socket_stream_client() does not populate errNum, or $errStr when there are
* connection errors, as in the case of SSL verification failure.
Expand Down Expand Up @@ -405,6 +446,4 @@ public function enableCrypto($type, $clientOrServer = 'client', $enable = true)
$this->setLastError(null, $errorMessage);
throw new SocketException($errorMessage);
}

}

3 changes: 2 additions & 1 deletion lib/Cake/Network/Email/SmtpTransport.php
Expand Up @@ -118,7 +118,8 @@ public function config($config = null) {
'username' => null,
'password' => null,
'client' => null,
'tls' => false
'tls' => false,
'ssl_allow_self_signed' => false
);
$this->_config = array_merge($default, $this->_config, $config);
return $this->_config;
Expand Down
47 changes: 3 additions & 44 deletions lib/Cake/Network/Http/HttpSocket.php
Expand Up @@ -72,7 +72,7 @@ class HttpSocket extends CakeSocket {
* Contain information about the last response (read only)
*
* @var array
*/
*/
public $response = null;

/**
Expand Down Expand Up @@ -361,8 +361,6 @@ public function request($request = array()) {
return false;
}

$this->_configContext($this->request['uri']['host']);

$this->request['raw'] = '';
if ($this->request['line'] !== false) {
$this->request['raw'] = $this->request['line'];
Expand All @@ -374,6 +372,8 @@ public function request($request = array()) {

$this->request['raw'] .= "\r\n";
$this->request['raw'] .= $this->request['body'];

// SSL context is set during the connect() method.
$this->write($this->request['raw']);

$response = null;
Expand Down Expand Up @@ -700,47 +700,6 @@ protected function _configUri($uri = null) {
return true;
}

/**
* Configure the socket's context. Adds in configuration
* that can not be declared in the class definition.
*
* @param string $host The host you're connecting to.
* @return void
*/
protected function _configContext($host) {
foreach ($this->config as $key => $value) {
if (substr($key, 0, 4) !== 'ssl_') {
continue;
}
$contextKey = substr($key, 4);
if (empty($this->config['context']['ssl'][$contextKey])) {
$this->config['context']['ssl'][$contextKey] = $value;
}
unset($this->config[$key]);
}
if (version_compare(PHP_VERSION, '5.3.2', '>=')) {
if (!isset($this->config['context']['ssl']['SNI_enabled'])) {
$this->config['context']['ssl']['SNI_enabled'] = true;
}
if (version_compare(PHP_VERSION, '5.6.0', '>=')) {
if (empty($this->config['context']['ssl']['peer_name'])) {
$this->config['context']['ssl']['peer_name'] = $host;
}
} else {
if (empty($this->config['context']['ssl']['SNI_server_name'])) {
$this->config['context']['ssl']['SNI_server_name'] = $host;
}
}
}
if (empty($this->config['context']['ssl']['cafile'])) {
$this->config['context']['ssl']['cafile'] = CAKE . 'Config' . DS . 'cacert.pem';
}
if (!empty($this->config['context']['ssl']['verify_host'])) {
$this->config['context']['ssl']['CN_match'] = $host;
}
unset($this->config['context']['ssl']['verify_host']);
}

/**
* Takes a $uri array and turns it into a fully qualified URL string
*
Expand Down
32 changes: 31 additions & 1 deletion lib/Cake/Test/Case/Network/CakeSocketTest.php
Expand Up @@ -367,7 +367,37 @@ public function testGetContext() {
$this->Socket = new CakeSocket($config);
$this->Socket->connect();
$result = $this->Socket->context();
$this->assertEquals($config['context'], $result);
$this->assertSame($config['context']['ssl']['capture_peer'], $result['ssl']['capture_peer']);
}

/**
* test configuring the context from the flat keys.
*
* @return void
*/
public function testConfigContext() {
$this->skipIf(!extension_loaded('openssl'), 'OpenSSL is not enabled cannot test SSL.');
$config = array(
'host' => 'smtp.gmail.com',
'port' => 465,
'timeout' => 5,
'ssl_verify_peer' => true,
'ssl_allow_self_signed' => false,
'ssl_verify_depth' => 5,
'ssl_verify_host' => true,
);
$this->Socket = new CakeSocket($config);

$this->Socket->connect();
$result = $this->Socket->context();

$this->assertTrue($result['ssl']['verify_peer']);
$this->assertFalse($result['ssl']['allow_self_signed']);
$this->assertEquals(5, $result['ssl']['verify_depth']);
$this->assertEquals('smtp.gmail.com', $result['ssl']['CN_match']);
$this->assertArrayNotHasKey('ssl_verify_peer', $this->Socket->config);
$this->assertArrayNotHasKey('ssl_allow_self_signed', $this->Socket->config);
$this->assertArrayNotHasKey('ssl_verify_host', $this->Socket->config);
$this->assertArrayNotHasKey('ssl_verify_depth', $this->Socket->config);
}
}
3 changes: 2 additions & 1 deletion lib/Cake/Test/Case/Network/Email/CakeEmailTest.php
Expand Up @@ -946,7 +946,8 @@ public function testConfigMerge() {
'username' => null,
'password' => null,
'client' => null,
'tls' => false
'tls' => false,
'ssl_allow_self_signed' => false
);
$this->assertEquals($expected, $this->CakeEmail->transportClass()->config());

Expand Down
43 changes: 4 additions & 39 deletions lib/Cake/Test/Case/Network/Http/HttpSocketTest.php
Expand Up @@ -314,23 +314,6 @@ public function testRequest() {
$response = $this->Socket->request(true);
$this->assertFalse($response);

$context = array(
'ssl' => array(
'verify_peer' => true,
'allow_self_signed' => false,
'verify_depth' => 5,
'SNI_enabled' => true,
'CN_match' => 'www.cakephp.org',
'cafile' => CAKE . 'Config' . DS . 'cacert.pem'
)
);

if (version_compare(PHP_VERSION, '5.6.0', '>=')) {
$context['ssl']['peer_name'] = 'www.cakephp.org';
} else {
$context['ssl']['SNI_server_name'] = 'www.cakephp.org';
}

$tests = array(
array(
'request' => 'http://www.cakephp.org/?foo=bar',
Expand All @@ -341,7 +324,10 @@ public function testRequest() {
'protocol' => 'tcp',
'port' => 80,
'timeout' => 30,
'context' => $context,
'ssl_verify_peer' => true,
'ssl_allow_self_signed' => false,
'ssl_verify_depth' => 5,
'ssl_verify_host' => true,
'request' => array(
'uri' => array(
'scheme' => 'http',
Expand Down Expand Up @@ -1843,27 +1829,6 @@ public function testPartialReset() {
$this->assertEquals(true, $return);
}

/**
* test configuring the context from the flat keys.
*
* @return void
*/
public function testConfigContext() {
$this->Socket->expects($this->any())
->method('read')->will($this->returnValue(false));

$this->Socket->reset();
$this->Socket->request('http://example.com');
$this->assertTrue($this->Socket->config['context']['ssl']['verify_peer']);
$this->assertFalse($this->Socket->config['context']['ssl']['allow_self_signed']);
$this->assertEquals(5, $this->Socket->config['context']['ssl']['verify_depth']);
$this->assertEquals('example.com', $this->Socket->config['context']['ssl']['CN_match']);
$this->assertArrayNotHasKey('ssl_verify_peer', $this->Socket->config);
$this->assertArrayNotHasKey('ssl_allow_self_signed', $this->Socket->config);
$this->assertArrayNotHasKey('ssl_verify_host', $this->Socket->config);
$this->assertArrayNotHasKey('ssl_verify_depth', $this->Socket->config);
}

/**
* Test that requests fail when peer verification fails.
*
Expand Down

0 comments on commit 94aeee4

Please sign in to comment.