Skip to content

Commit

Permalink
Detect old NSS and OpenSSL versions
Browse files Browse the repository at this point in the history
This will detect old NSS and OpenSSL versions and show appropriate errors in the admin interface.

Fixes #17901
  • Loading branch information
LukasReschke committed Jul 27, 2015
1 parent c030ae9 commit eed4ef1
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 10 deletions.
3 changes: 3 additions & 0 deletions core/js/setupchecks.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@
t('core', '/dev/urandom is not readable by PHP which is highly discouraged for security reasons. Further information can be found in our <a href="{docLink}">documentation</a>.', {docLink: data.securityDocs})
);
}
if(data.isUsedTlsLibOutdated) {
messages.push(data.isUsedTlsLibOutdated);
}
} else {
messages.push(t('core', 'Error occurred while checking server setup'));
}
Expand Down
3 changes: 2 additions & 1 deletion settings/application.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ public function __construct(array $urlParams=[]){
$c->query('Config'),
$c->query('ClientService'),
$c->query('URLGenerator'),
$c->query('Util')
$c->query('Util'),
$c->query('L10N')
);
});

Expand Down
65 changes: 64 additions & 1 deletion settings/controller/checksetupcontroller.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@

namespace OC\Settings\Controller;

use GuzzleHttp\Exception\ClientException;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\DataResponse;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
use OC_Util;
use OCP\IURLGenerator;
Expand All @@ -43,6 +45,8 @@ class CheckSetupController extends Controller {
private $util;
/** @var IURLGenerator */
private $urlGenerator;
/** @var IL10N */
private $l10n;

/**
* @param string $AppName
Expand All @@ -51,18 +55,21 @@ class CheckSetupController extends Controller {
* @param IClientService $clientService
* @param IURLGenerator $urlGenerator
* @param \OC_Util $util
* @param IL10N $l10n
*/
public function __construct($AppName,
IRequest $request,
IConfig $config,
IClientService $clientService,
IURLGenerator $urlGenerator,
\OC_Util $util) {
\OC_Util $util,
IL10N $l10n) {
parent::__construct($AppName, $request);
$this->config = $config;
$this->clientService = $clientService;
$this->util = $util;
$this->urlGenerator = $urlGenerator;
$this->l10n = $l10n;
}

/**
Expand Down Expand Up @@ -109,6 +116,61 @@ private function isUrandomAvailable() {
return false;
}

/**
* Public for the sake of unit-testing
*
* @return array
*/
public function getCurlVersion() {
return curl_version();
}

/**
* Check if the used SSL lib is outdated. Older OpenSSL and NSS versions do
* have multiple bugs which likely lead to problems in combination with
* functionalities required by ownCloud such as SNI.
*
* @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
* @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
* @return string
*/
private function isUsedTlsLibOutdated() {
$versionString = $this->getCurlVersion();
if(isset($versionString['ssl_version'])) {
$versionString = $versionString['ssl_version'];
} else {
return '';
}

// Check if at least OpenSSL after 1.01d or 1.0.2b
if(strpos($versionString, 'OpenSSL/') === 0) {
$majorVersion = substr($versionString, 8, 5);
$patchRelease = substr($versionString, 13, 6);

if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system. Without this features such as the app store or Federated Cloud Sharing will not work reliable.', ['OpenSSL', $versionString]);
}
}

// Check if NSS and perform heuristic check
if(strpos($versionString, 'NSS/') === 0) {
try {
$firstClient = $this->clientService->newClient();
$firstClient->get('https://www.owncloud.org/');

$secondClient = $this->clientService->newClient();
$secondClient->get('https://owncloud.org/');
} catch (ClientException $e) {
if($e->getResponse()->getStatusCode() === 400) {
return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system. Without this features such as the app store or Federated Cloud Sharing will not work reliable.', ['NSS', $versionString]);
}
}
}

return '';
}

/**
* @return DataResponse
*/
Expand All @@ -121,6 +183,7 @@ public function check() {
'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
'isUrandomAvailable' => $this->isUrandomAvailable(),
'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
]
);
}
Expand Down
168 changes: 160 additions & 8 deletions tests/settings/controller/CheckSetupControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@

namespace OC\Settings\Controller;

use GuzzleHttp\Exception\ClientException;
use OCP\AppFramework\Http\DataResponse;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IURLGenerator;
use OC_Util;
Expand All @@ -47,6 +49,8 @@ class CheckSetupControllerTest extends TestCase {
private $urlGenerator;
/** @var OC_Util */
private $util;
/** @var IL10N */
private $l10n;

public function setUp() {
parent::setUp();
Expand All @@ -63,15 +67,20 @@ public function setUp() {
->disableOriginalConstructor()->getMock();
$this->urlGenerator = $this->getMockBuilder('\OCP\IURLGenerator')
->disableOriginalConstructor()->getMock();
$this->l10n = $this->getMockBuilder('\OCP\IL10N')
->disableOriginalConstructor()->getMock();

$this->checkSetupController = new CheckSetupController(
'settings',
$this->request,
$this->config,
$this->clientService,
$this->urlGenerator,
$this->util
);
$this->checkSetupController = $this->getMockBuilder('\OC\Settings\Controller\CheckSetupController')
->setConstructorArgs([
'settings',
$this->request,
$this->config,
$this->clientService,
$this->urlGenerator,
$this->util,
$this->l10n,
])
->setMethods(['getCurlVersion'])->getMock();
}

public function testIsInternetConnectionWorkingDisabledViaConfig() {
Expand Down Expand Up @@ -241,8 +250,151 @@ public function testCheck() {
'memcacheDocs' => 'http://doc.owncloud.org/server/go.php?to=admin-performance',
'isUrandomAvailable' => self::invokePrivate($this->checkSetupController, 'isUrandomAvailable'),
'securityDocs' => 'https://doc.owncloud.org/server/8.1/admin_manual/configuration_server/hardening.html',
'isUsedTlsLibOutdated' => '',
]
);
$this->assertEquals($expected, $this->checkSetupController->check());
}

public function testGetCurlVersion() {
$checkSetupController = $this->getMockBuilder('\OC\Settings\Controller\CheckSetupController')
->setConstructorArgs([
'settings',
$this->request,
$this->config,
$this->clientService,
$this->urlGenerator,
$this->util,
$this->l10n,
])
->setMethods(null)->getMock();

$this->assertArrayHasKey('ssl_version', $checkSetupController->getCurlVersion());
}

public function testIsUsedTlsLibOutdatedWithAnotherLibrary() {
$this->checkSetupController
->expects($this->once())
->method('getCurlVersion')
->will($this->returnValue(['ssl_version' => 'SSLlib']));
$this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated'));
}

public function testIsUsedTlsLibOutdatedWithMisbehavingCurl() {
$this->checkSetupController
->expects($this->once())
->method('getCurlVersion')
->will($this->returnValue([]));
$this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated'));
}

public function testIsUsedTlsLibOutdatedWithOlderOpenSsl() {
$this->checkSetupController
->expects($this->once())
->method('getCurlVersion')
->will($this->returnValue(['ssl_version' => 'OpenSSL/1.0.1c']));
$this->l10n
->expects($this->once())
->method('t')
->with('cURL is using an outdated %s version (%s). Please update your operating system. Without this features such as the app store or Federated Cloud Sharing will not work reliable.', ['OpenSSL', 'OpenSSL/1.0.1c'])
->will($this->returnValue('cURL is using an outdated OpenSSL version (OpenSSL/1.0.1c). Please update your operating system. Without this features such as the app store or Federated Cloud Sharing will not work reliable.'));
$this->assertSame('cURL is using an outdated OpenSSL version (OpenSSL/1.0.1c). Please update your operating system. Without this features such as the app store or Federated Cloud Sharing will not work reliable.', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated'));
}

public function testIsUsedTlsLibOutdatedWithOlderOpenSsl1() {
$this->checkSetupController
->expects($this->once())
->method('getCurlVersion')
->will($this->returnValue(['ssl_version' => 'OpenSSL/1.0.2a']));
$this->l10n
->expects($this->once())
->method('t')
->with('cURL is using an outdated %s version (%s). Please update your operating system. Without this features such as the app store or Federated Cloud Sharing will not work reliable.', ['OpenSSL', 'OpenSSL/1.0.2a'])
->will($this->returnValue('cURL is using an outdated OpenSSL version (OpenSSL/1.0.2a). Please update your operating system. Without this features such as the app store or Federated Cloud Sharing will not work reliable.'));
$this->assertSame('cURL is using an outdated OpenSSL version (OpenSSL/1.0.2a). Please update your operating system. Without this features such as the app store or Federated Cloud Sharing will not work reliable.', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated'));
}

public function testIsUsedTlsLibOutdatedWithMatchingOpenSslVersion() {
$this->checkSetupController
->expects($this->once())
->method('getCurlVersion')
->will($this->returnValue(['ssl_version' => 'OpenSSL/1.0.1d']));
$this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated'));
}

public function testIsUsedTlsLibOutdatedWithMatchingOpenSslVersion1() {
$this->checkSetupController
->expects($this->once())
->method('getCurlVersion')
->will($this->returnValue(['ssl_version' => 'OpenSSL/1.0.2b']));
$this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated'));
}

public function testIsBuggyNss400() {
$this->checkSetupController
->expects($this->once())
->method('getCurlVersion')
->will($this->returnValue(['ssl_version' => 'NSS/1.0.2b']));
$client = $this->getMockBuilder('\OCP\Http\Client\IClient')
->disableOriginalConstructor()->getMock();
$exception = $this->getMockBuilder('\GuzzleHttp\Exception\ClientException')
->disableOriginalConstructor()->getMock();
$response = $this->getMockBuilder('\GuzzleHttp\Message\ResponseInterface')
->disableOriginalConstructor()->getMock();
$response->expects($this->once())
->method('getStatusCode')
->will($this->returnValue(400));
$exception->expects($this->once())
->method('getResponse')
->will($this->returnValue($response));

$client->expects($this->at(0))
->method('get')
->with('https://www.owncloud.org/', [])
->will($this->throwException($exception));

$this->clientService->expects($this->once())
->method('newClient')
->will($this->returnValue($client));

$this->l10n
->expects($this->once())
->method('t')
->with('cURL is using an outdated %s version (%s). Please update your operating system. Without this features such as the app store or Federated Cloud Sharing will not work reliable.', ['NSS', 'NSS/1.0.2b'])
->will($this->returnValue('cURL is using an outdated NSS version (NSS/1.0.2b). Please update your operating system. Without this features such as the app store or Federated Cloud Sharing will not work reliable.'));


$this->assertSame('cURL is using an outdated NSS version (NSS/1.0.2b). Please update your operating system. Without this features such as the app store or Federated Cloud Sharing will not work reliable.', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated'));
}


public function testIsBuggyNss200() {
$this->checkSetupController
->expects($this->once())
->method('getCurlVersion')
->will($this->returnValue(['ssl_version' => 'NSS/1.0.2b']));
$client = $this->getMockBuilder('\OCP\Http\Client\IClient')
->disableOriginalConstructor()->getMock();
$exception = $this->getMockBuilder('\GuzzleHttp\Exception\ClientException')
->disableOriginalConstructor()->getMock();
$response = $this->getMockBuilder('\GuzzleHttp\Message\ResponseInterface')
->disableOriginalConstructor()->getMock();
$response->expects($this->once())
->method('getStatusCode')
->will($this->returnValue(200));
$exception->expects($this->once())
->method('getResponse')
->will($this->returnValue($response));

$client->expects($this->at(0))
->method('get')
->with('https://www.owncloud.org/', [])
->will($this->throwException($exception));

$this->clientService->expects($this->once())
->method('newClient')
->will($this->returnValue($client));

$this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated'));
}
}

0 comments on commit eed4ef1

Please sign in to comment.