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 28, 2015
1 parent c34e63b commit 0d515de
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 11 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
70 changes: 69 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,66 @@ 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 '';
}

$features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
if(OC_Util::getEditionString() !== '') {
$features = (string)$this->l10n->t('Federated Cloud Sharing');
}

// 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 or features such as %s will not work reliably.', ['OpenSSL', $versionString, $features]);
}
}

// 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 or features such as %s will not work reliably.', ['NSS', $versionString, $features]);
}
}
}

return '';
}

/**
* @return DataResponse
*/
Expand All @@ -121,6 +188,7 @@ public function check() {
'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
'isUrandomAvailable' => $this->isUrandomAvailable(),
'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
]
);
}
Expand Down
156 changes: 147 additions & 9 deletions tests/settings/controller/CheckSetupControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
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 +48,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 +66,24 @@ public function setUp() {
->disableOriginalConstructor()->getMock();
$this->urlGenerator = $this->getMockBuilder('\OCP\IURLGenerator')
->disableOriginalConstructor()->getMock();

$this->checkSetupController = new CheckSetupController(
'settings',
$this->request,
$this->config,
$this->clientService,
$this->urlGenerator,
$this->util
);
$this->l10n = $this->getMockBuilder('\OCP\IL10N')
->disableOriginalConstructor()->getMock();
$this->l10n->expects($this->any())
->method('t')
->will($this->returnCallback(function($message, array $replace) {
return vsprintf($message, $replace);
}));
$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 +253,134 @@ 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->assertSame('cURL is using an outdated OpenSSL version (OpenSSL/1.0.1c). Please update your operating system or features such as installing and updating apps via the app store or Federated Cloud Sharing will not work reliably.', $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->assertSame('cURL is using an outdated OpenSSL version (OpenSSL/1.0.2a). Please update your operating system or features such as installing and updating apps via the app store or Federated Cloud Sharing will not work reliably.', $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->assertSame('cURL is using an outdated NSS version (NSS/1.0.2b). Please update your operating system or features such as installing and updating apps via the app store or Federated Cloud Sharing will not work reliably.', $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 0d515de

Please sign in to comment.