summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukas Reschke <lukas@owncloud.com>2014-08-27 16:28:51 +0200
committerRobin Appelman <icewind@owncloud.com>2014-08-31 10:47:50 +0200
commit4efe6f62402482608cb1b2f4c51b9b3e41603733 (patch)
tree371c210240a69df23e0a732d8f45dd0993fa5bb9
parent1361bbb1e6a47266cf3a11b2ddba77706522d9e0 (diff)
downloadnextcloud-server-4efe6f62402482608cb1b2f4c51b9b3e41603733.tar.gz
nextcloud-server-4efe6f62402482608cb1b2f4c51b9b3e41603733.zip
Add unit tests and fix rootcerts creation bug
-rw-r--r--lib/private/security/certificate.php27
-rw-r--r--lib/private/security/certificatemanager.php37
-rw-r--r--settings/ajax/addRootCertificate.php7
-rw-r--r--settings/js/personal.js2
-rw-r--r--settings/templates/personal.php9
-rw-r--r--tests/data/certificates/badCertificate.crt13
-rw-r--r--tests/data/certificates/expiredCertificate.crt13
-rw-r--r--tests/data/certificates/goodCertificate.crt15
-rw-r--r--tests/lib/security/certificate.php90
-rw-r--r--tests/lib/security/certificatemanager.php87
10 files changed, 263 insertions, 37 deletions
diff --git a/lib/private/security/certificate.php b/lib/private/security/certificate.php
index 953111f469d..63c02a124f4 100644
--- a/lib/private/security/certificate.php
+++ b/lib/private/security/certificate.php
@@ -30,17 +30,22 @@ class Certificate implements ICertificate {
/**
* @param string $data base64 encoded certificate
* @param string $name
+ * @throws \Exception If the certificate could not get parsed
*/
public function __construct($data, $name) {
$this->name = $name;
- $info = openssl_x509_parse($data);
- $this->commonName = $info['subject']['CN'];
- $this->organization = isset($info['subject']['O']) ? $info['subject']['O'] : null;
- $this->serial = $this->formatSerial($info['serialNumber']);
- $this->issueDate = new \DateTime('@' . $info['validFrom_time_t']);
- $this->expireDate = new \DateTime('@' . $info['validTo_time_t']);
- $this->issuerName = $info['issuer']['CN'];
- $this->issuerOrganization = isset($info['issuer']['O']) ? $info['issuer']['O'] : null;
+ try {
+ $info = openssl_x509_parse($data);
+ $this->commonName = isset($info['subject']['CN']) ? $info['subject']['CN'] : null;
+ $this->organization = isset($info['subject']['O']) ? $info['subject']['O'] : null;
+ $this->serial = $this->formatSerial($info['serialNumber']);
+ $this->issueDate = new \DateTime('@' . $info['validFrom_time_t']);
+ $this->expireDate = new \DateTime('@' . $info['validTo_time_t']);
+ $this->issuerName = isset($info['issuer']['CN']) ? $info['issuer']['CN'] : null;
+ $this->issuerOrganization = isset($info['issuer']['O']) ? $info['issuer']['O'] : null;
+ } catch (\Exception $e) {
+ throw new \Exception('Certificate could not get parsed.');
+ }
}
/**
@@ -62,7 +67,7 @@ class Certificate implements ICertificate {
}
/**
- * @return string
+ * @return string|null
*/
public function getCommonName() {
return $this->commonName;
@@ -105,14 +110,14 @@ class Certificate implements ICertificate {
}
/**
- * @return string
+ * @return string|null
*/
public function getIssuerName() {
return $this->issuerName;
}
/**
- * @return string
+ * @return string|null
*/
public function getIssuerOrganization() {
return $this->issuerOrganization;
diff --git a/lib/private/security/certificatemanager.php b/lib/private/security/certificatemanager.php
index 64a1d6431a4..cae9730eb26 100644
--- a/lib/private/security/certificatemanager.php
+++ b/lib/private/security/certificatemanager.php
@@ -44,7 +44,9 @@ class CertificateManager implements ICertificateManager {
}
while (false !== ($file = readdir($handle))) {
if ($file != '.' && $file != '..') {
- $result[] = new Certificate(file_get_contents($path . $file), $file);
+ try {
+ $result[] = new Certificate(file_get_contents($path . $file), $file);
+ } catch(\Exception $e) {}
}
}
return $result;
@@ -59,7 +61,7 @@ class CertificateManager implements ICertificateManager {
$fh_certs = fopen($path . '/rootcerts.crt', 'w');
foreach ($certs as $cert) {
- $file = $path . '/uploads/' . $cert;
+ $file = $path . '/uploads/' . $cert->getName();
$data = file_get_contents($file);
if (strpos($data, 'BEGIN CERTIFICATE')) {
fwrite($fh_certs, $data);
@@ -75,35 +77,32 @@ class CertificateManager implements ICertificateManager {
*
* @param string $certificate the certificate data
* @param string $name the filename for the certificate
- * @return bool | \OCP\ICertificate
+ * @return \OCP\ICertificate|void|bool
+ * @throws \Exception If the certificate could not get added
*/
public function addCertificate($certificate, $name) {
if (!Filesystem::isValidPath($name) or Filesystem::isFileBlacklisted($name)) {
return false;
}
- $isValid = openssl_pkey_get_public($certificate);
- if (!$isValid) {
- $data = chunk_split(base64_encode($certificate), 64, "\n");
- $data = "-----BEGIN CERTIFICATE-----\n" . $data . "-----END CERTIFICATE-----\n";
- $isValid = openssl_pkey_get_public($data);
+ $dir = $this->user->getHome() . '/files_external/uploads/';
+ if (!file_exists($dir)) {
+ //path might not exist (e.g. non-standard OC_User::getHome() value)
+ //in this case create full path using 3rd (recursive=true) parameter.
+ //note that we use "normal" php filesystem functions here since the certs need to be local
+ mkdir($dir, 0700, true);
}
- if ($isValid) {
- $dir = $this->user->getHome() . '/files_external/uploads/';
- if (!file_exists($dir)) {
- //path might not exist (e.g. non-standard OC_User::getHome() value)
- //in this case create full path using 3rd (recursive=true) parameter.
- //note that we use "normal" php filesystem functions here since the certs need to be local
- mkdir($dir, 0700, true);
- }
+ try {
$file = $dir . $name;
+ $certificateObject = new Certificate($certificate, $name);
file_put_contents($file, $certificate);
$this->createCertificateBundle();
- return new Certificate($certificate, $name);
- } else {
- return false;
+ return $certificateObject;
+ } catch (\Exception $e) {
+ throw $e;
}
+
}
/**
diff --git a/settings/ajax/addRootCertificate.php b/settings/ajax/addRootCertificate.php
index 87b1460ef12..378ef39c1e5 100644
--- a/settings/ajax/addRootCertificate.php
+++ b/settings/ajax/addRootCertificate.php
@@ -1,4 +1,5 @@
<?php
+OCP\JSON::checkLoggedIn();
OCP\JSON::callCheck();
$l = new OC_L10N('core');
@@ -13,8 +14,8 @@ $filename = basename($_FILES['rootcert_import']['name']);
$certificateManager = \OC::$server->getCertificateManager();
-$cert = $certificateManager->addCertificate($data, $filename);
-if ($cert) {
+try {
+ $cert = $certificateManager->addCertificate($data, $filename);
OCP\JSON::success(array(
'name' => $cert->getName(),
'commonName' => $cert->getCommonName(),
@@ -26,6 +27,6 @@ if ($cert) {
'issuer' => $cert->getIssuerName(),
'issuerOrganization' => $cert->getIssuerOrganization()
));
-} else {
+} catch(\Exception $e) {
OCP\JSON::error(array('error' => 'Couldn\'t import SSL root certificate, allowed formats: PEM and DER'));
}
diff --git a/settings/js/personal.js b/settings/js/personal.js
index d6546717d16..11e9593d74a 100644
--- a/settings/js/personal.js
+++ b/settings/js/personal.js
@@ -325,7 +325,7 @@ $(document).ready(function () {
var row = $('<tr/>');
row.addClass(isExpired? 'expired': 'valid');
row.append($('<td/>').attr('title', data.result.organization).text(data.result.commonName));
- row.append($('<td/>').attr('title', t('core,', 'Valid from {date}', {date: data.result.validFromString}))
+ row.append($('<td/>').attr('title', t('core,', 'Valid until {date}', {date: data.result.validFromString}))
.text(data.result.validTillString));
row.append($('<td/>').attr('title', data.result.issuerOrganization).text(data.result.issuer));
row.append($('<td/>').addClass('remove').append(
diff --git a/settings/templates/personal.php b/settings/templates/personal.php
index 8cb7be27735..871a0ec9e39 100644
--- a/settings/templates/personal.php
+++ b/settings/templates/personal.php
@@ -2,7 +2,10 @@
* Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
- */?>
+ */
+
+/** @var $_ array */
+?>
<div class="clientsbox center">
<h2><?php p($l->t('Get the apps to sync your files'));?></h2>
@@ -160,12 +163,12 @@ if($_['passwordChangeSupported']) {
<th/>
</thead>
<tbody>
- <?php foreach ($_['certs'] as $rootCert): /**@var \OCP\ICertificate $rootCert*/?>
+ <?php foreach ($_['certs'] as $rootCert): /**@var \OCP\ICertificate $rootCert*/ ?>
<tr class="<?php echo ($rootCert->isExpired()) ? 'expired' : 'valid' ?>" data-name="<?php p($rootCert->getName()) ?>">
<td class="rootCert" title="<?php p($rootCert->getOrganization())?>">
<?php p($rootCert->getCommonName()) ?>
</td>
- <td title="<?php p($l->t('Valid from %s', $l->l('date', $rootCert->getExpireDate()))) ?>">
+ <td title="<?php p($l->t('Valid until %s', $l->l('date', $rootCert->getExpireDate()))) ?>">
<?php echo $l->l('date', $rootCert->getExpireDate()) ?>
</td>
<td title="<?php p($rootCert->getIssuerOrganization()) ?>">
diff --git a/tests/data/certificates/badCertificate.crt b/tests/data/certificates/badCertificate.crt
new file mode 100644
index 00000000000..dcb1895fbad
--- /dev/null
+++ b/tests/data/certificates/badCertificate.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICATCCAWoCCQDNdmb4pJrUeDANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
+VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMB4XDTE0MDgyNzA4NDg1MVoXDTE1MDgyNzA4NDg1MVowRTELMAkG
+A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
+IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvrMe
+x5D45HVMV2U4kqTU0mzHAihHT6r+OtO6g7S9yIlJZGGVcEet6An78Ow7aYM141eI
+Jfbvqql7OIblHXSw7mvkw4bOQ1ee5lmJYOYCgaMNJ6mBLJfpK9xwidb0ZvhWOA8P
+DLIiBKA3T5ChXCzilD5GF2+H/BXBE9lL9tuDjM0CAwEAATANBgkqhkiG9w0BAQUF
+AAOBgQCJwfJe7j+aNkopw+P8uxobfOnMWU9XC4Pu+39TVLeakeSqu2Y8vJSHmkjF
+WK3VXAJr33Eul5VP/3SWGwuRPd9X4i4iLh1gJfYvi9MJf1lQNYncGCM+xtdrNu2O
+u0yexkOBRrapDYjcv58BiOaFgvFLquKvtVj9HlcYRfwfM77uKQ==
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/tests/data/certificates/expiredCertificate.crt b/tests/data/certificates/expiredCertificate.crt
new file mode 100644
index 00000000000..5e7e5df2cbf
--- /dev/null
+++ b/tests/data/certificates/expiredCertificate.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICATCCAWoCCQCjCIB6tCZ2sDANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
+VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMB4XDTE0MDgyNzA5MTI0M1oXDTE0MDgyODA5MTI0M1owRTELMAkG
+A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
+IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvrMe
+x5D45HVMV2U4kqTU0mzHAihHT6r+OtO6g7S9yIlJZGGVcEet6An78Ow7aYM141eI
+Jfbvqql7OIblHXSw7mvkw4bOQ1ee5lmJYOYCgaMNJ6mBLJfpK9xwidb0ZvhWOA8P
+DLIiBKA3T5ChXCzilD5GF2+H/BXBE9lL9tuDjM0CAwEAATANBgkqhkiG9w0BAQUF
+AAOBgQBuNClmOj3wudlX86nygcZgQT2+ZS8f1iJgM9lbrrkenT6tgcT1/YjcrN9C
+BZR29Wz7htflpqverLUGZXh72K+gYercyR16Zu7zjt/NWuZldZmzJ3bUGq2HSoCX
+2sDykAEuaDxUlzdJrztlOH4vPlRaGbxUogpC2hB1BQfxA90CIA==
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/tests/data/certificates/goodCertificate.crt b/tests/data/certificates/goodCertificate.crt
new file mode 100644
index 00000000000..4a5d7bd32fe
--- /dev/null
+++ b/tests/data/certificates/goodCertificate.crt
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICazCCAdQCCQCySF7HjQD78DANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJD
+SDEPMA0GA1UECBMGWnVyaWNoMQ8wDQYDVQQHEwZadXJpY2gxFjAUBgNVBAoTDW93
+bkNsb3VkIEluYy4xETAPBgNVBAsTCFNlY3VyaXR5MR4wHAYDVQQDExVzZWN1cml0
+eS5vd25jbG91ZC5jb20wHhcNMTQwODI3MDg0NTUyWhcNMTUwODI3MDg0NTUyWjB6
+MQswCQYDVQQGEwJDSDEPMA0GA1UECBMGWnVyaWNoMQ8wDQYDVQQHEwZadXJpY2gx
+FjAUBgNVBAoTDW93bkNsb3VkIEluYy4xETAPBgNVBAsTCFNlY3VyaXR5MR4wHAYD
+VQQDExVzZWN1cml0eS5vd25jbG91ZC5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0A
+MIGJAoGBAL55lB4RvU0pTyh7YsLCxPBq43xxkRZBxfZENoflCIUsBo7/mXNz2zVO
+476oQ4L47heUOX3j8kemOgPmWEqA34JB8rusijCy5WqFBLnm4HsRLa66i+Jgd+Yl
+QhcKvhGas1K/CVTG4oSLoAmA2coZUL94uxnRtd8aluflHMNGApIlAgMBAAEwDQYJ
+KoZIhvcNAQEFBQADgYEADo08zWdOtIvCKFDnLbzRwIjSYTlAtQtQaULv7KQe3qIn
+iaFAi6fAynHfdC8/2tvmSeniw0OZBkrfVGIVtUbwCSrljNSUY/lWrUR0pE61lb4r
+DpX0JZjlk48XEaErRVDfu3wq6n/2nYg6HnaLOPwt8OSYYrxzvXlFPrKBH3q6R+M=
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/tests/lib/security/certificate.php b/tests/lib/security/certificate.php
new file mode 100644
index 00000000000..694d1f27011
--- /dev/null
+++ b/tests/lib/security/certificate.php
@@ -0,0 +1,90 @@
+<?php
+/**
+ * Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+*/
+
+use \OC\Security\Certificate;
+
+class CertificateTest extends \PHPUnit_Framework_TestCase {
+
+ /** @var Certificate That contains a valid certificate */
+ protected $goodCertificate;
+ /** @var Certificate That contains an invalid certificate */
+ protected $invalidCertificate;
+ /** @var Certificate That contains an expired certificate */
+ protected $expiredCertificate;
+
+ function setUp() {
+ $goodCertificate = file_get_contents(__DIR__.'/../../data/certificates/goodCertificate.crt');
+ $this->goodCertificate = new Certificate($goodCertificate, 'GoodCertificate');
+ $badCertificate = file_get_contents(__DIR__.'/../../data/certificates/badCertificate.crt');
+ $this->invalidCertificate = new Certificate($badCertificate, 'BadCertificate');
+ $expiredCertificate = file_get_contents(__DIR__.'/../../data/certificates/expiredCertificate.crt');
+ $this->expiredCertificate = new Certificate($expiredCertificate, 'ExpiredCertificate');
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Certificate could not get parsed.
+ */
+ function testBogusData() {
+ new Certificate('foo', 'bar');
+ }
+
+ function testGetName() {
+ $this->assertSame('GoodCertificate', $this->goodCertificate->getName());
+ $this->assertSame('BadCertificate', $this->invalidCertificate->getName());
+ }
+
+ function testGetCommonName() {
+ $this->assertSame('security.owncloud.com', $this->goodCertificate->getCommonName());
+ $this->assertSame(null, $this->invalidCertificate->getCommonName());
+ }
+
+ function testGetOrganization() {
+ $this->assertSame('ownCloud Inc.', $this->goodCertificate->getOrganization());
+ $this->assertSame('Internet Widgits Pty Ltd', $this->invalidCertificate->getOrganization());
+ }
+
+ function testGetSerial() {
+ $this->assertSame('7F:FF:FF:FF:FF:FF:FF:FF', $this->goodCertificate->getSerial());
+ $this->assertSame('7F:FF:FF:FF:FF:FF:FF:FF', $this->invalidCertificate->getSerial());
+ }
+
+ function testGetIssueDate() {
+ $this->assertEquals(new DateTime('2014-08-27 08:45:52'), $this->goodCertificate->getIssueDate());
+ $this->assertEquals(new DateTime('2014-08-27 08:48:51'), $this->invalidCertificate->getIssueDate());
+ }
+
+ function testGetExpireDate() {
+ $this->assertEquals(new DateTime('2015-08-27 08:45:52'), $this->goodCertificate->getExpireDate());
+ $this->assertEquals(new DateTime('2015-08-27 08:48:51'), $this->invalidCertificate->getExpireDate());
+ $this->assertEquals(new DateTime('2014-08-28 09:12:43'), $this->expiredCertificate->getExpireDate());
+ }
+
+ /**
+ * Obviously the following test case might fail after 2015-08-27, just create a new certificate with longer validity then
+ */
+ function testIsExpired() {
+ $this->assertSame(false, $this->goodCertificate->isExpired());
+ $this->assertSame(false, $this->invalidCertificate->isExpired());
+
+ // TODO: Change to false after tomorrow
+ $this->assertSame(false, $this->expiredCertificate->isExpired());
+ }
+
+ function testGetIssuerName() {
+ $this->assertSame('security.owncloud.com', $this->goodCertificate->getIssuerName());
+ $this->assertSame(null, $this->invalidCertificate->getIssuerName());
+ $this->assertSame(null, $this->expiredCertificate->getIssuerName());
+ }
+
+ function testGetIssuerOrganization() {
+ $this->assertSame('ownCloud Inc.', $this->goodCertificate->getIssuerOrganization());
+ $this->assertSame('Internet Widgits Pty Ltd', $this->invalidCertificate->getIssuerOrganization());
+ $this->assertSame('Internet Widgits Pty Ltd', $this->expiredCertificate->getIssuerOrganization());
+ }
+} \ No newline at end of file
diff --git a/tests/lib/security/certificatemanager.php b/tests/lib/security/certificatemanager.php
new file mode 100644
index 00000000000..5baf9e16e81
--- /dev/null
+++ b/tests/lib/security/certificatemanager.php
@@ -0,0 +1,87 @@
+<?php
+/**
+ * Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+use \OC\Security\CertificateManager;
+
+class CertificateManagerTest extends \PHPUnit_Framework_TestCase {
+
+ /** @var CertificateManager */
+ private $certificateManager;
+ /** @var String */
+ private $username;
+ /** @var \OC\User\User */
+ private $user;
+
+ function setUp() {
+ $this->username = OC_Util::generateRandomBytes(20);
+ OC_User::createUser($this->username, OC_Util::generateRandomBytes(20));
+
+ \OC_Util::tearDownFS();
+ \OC_User::setUserId('');
+ \OC\Files\Filesystem::tearDown();
+ \OC_Util::setupFS($this->username);
+
+ $this->user = \OC::$server->getUserManager()->get($this->username);
+
+ $this->certificateManager = new CertificateManager($this->user);
+ }
+
+ function tearDown() {
+ \OC_User::deleteUser($this->username);
+ }
+
+ protected function assertEqualsArrays($expected, $actual) {
+ sort($expected);
+ sort($actual);
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ function testListCertificates() {
+ // Test empty certificate bundle
+ $this->assertSame(array(), $this->certificateManager->listCertificates());
+
+ // Add some certificates
+ $this->certificateManager->addCertificate(file_get_contents(__DIR__.'/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
+ $certificateStore = array();
+ $certificateStore[] = new \OC\Security\Certificate(file_get_contents(__DIR__.'/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
+ $this->assertEqualsArrays($certificateStore, $this->certificateManager->listCertificates());
+
+ // Add another certificates
+ $this->certificateManager->addCertificate(file_get_contents(__DIR__.'/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
+ $certificateStore[] = new \OC\Security\Certificate(file_get_contents(__DIR__.'/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
+ $this->assertEqualsArrays($certificateStore, $this->certificateManager->listCertificates());
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Certificate could not get parsed.
+ */
+ function testAddInvalidCertificate() {
+ $this->certificateManager->addCertificate('InvalidCertificate', 'invalidCertificate');
+ }
+
+ function testAddDangerousFile() {
+ $this->assertFalse($this->certificateManager->addCertificate(file_get_contents(__DIR__.'/../../data/certificates/expiredCertificate.crt'), '.htaccess'));
+ $this->assertFalse($this->certificateManager->addCertificate(file_get_contents(__DIR__.'/../../data/certificates/expiredCertificate.crt'), '../../foo.txt'));
+ }
+
+ function testRemoveDangerousFile() {
+ $this->assertFalse($this->certificateManager->removeCertificate('../../foo.txt'));
+ }
+
+ function testRemoveExistingFile() {
+ $this->certificateManager->addCertificate(file_get_contents(__DIR__.'/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
+ $this->assertTrue($this->certificateManager->removeCertificate('GoodCertificate'));
+ }
+
+ function testGetCertificateBundle() {
+ $this->assertSame($this->user->getHome().'/files_external/rootcerts.crt', $this->certificateManager->getCertificateBundle());
+ }
+
+} \ No newline at end of file