Skip to content

Commit 6c3dda4

Browse files
committed
New SSE key format
* Encrypt the keys with the instance secret * Store them as json (so we can add other things if needed) Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
1 parent 886466d commit 6c3dda4

10 files changed

Lines changed: 586 additions & 32 deletions

File tree

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
6+
*
7+
* @author Roeland Jago Douma <roeland@famdouma.nl>
8+
*
9+
* @license GNU AGPL version 3 or any later version
10+
*
11+
* This program is free software: you can redistribute it and/or modify
12+
* it under the terms of the GNU Affero General Public License as
13+
* published by the Free Software Foundation, either version 3 of the
14+
* License, or (at your option) any later version.
15+
*
16+
* This program is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
* GNU Affero General Public License for more details.
20+
*
21+
* You should have received a copy of the GNU Affero General Public License
22+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
23+
*
24+
*/
25+
26+
27+
namespace OC\Core\Command\Encryption;
28+
29+
use OC\Encryption\Keys\Storage;
30+
use OC\Encryption\Util;
31+
use OC\Files\View;
32+
use OCP\IConfig;
33+
use OCP\IUserManager;
34+
use OCP\Security\ICrypto;
35+
use Symfony\Component\Console\Command\Command;
36+
use Symfony\Component\Console\Helper\ProgressBar;
37+
use Symfony\Component\Console\Helper\QuestionHelper;
38+
use Symfony\Component\Console\Input\InputInterface;
39+
use Symfony\Component\Console\Output\OutputInterface;
40+
41+
class MigrateKeyStorage extends Command {
42+
43+
/** @var View */
44+
protected $rootView;
45+
46+
/** @var IUserManager */
47+
protected $userManager;
48+
49+
/** @var IConfig */
50+
protected $config;
51+
52+
/** @var Util */
53+
protected $util;
54+
55+
/** @var QuestionHelper */
56+
protected $questionHelper;
57+
/**
58+
* @var ICrypto
59+
*/
60+
private $crypto;
61+
62+
public function __construct(View $view, IUserManager $userManager, IConfig $config, Util $util, ICrypto $crypto) {
63+
parent::__construct();
64+
$this->rootView = $view;
65+
$this->userManager = $userManager;
66+
$this->config = $config;
67+
$this->util = $util;
68+
$this->crypto = $crypto;
69+
}
70+
71+
protected function configure() {
72+
parent::configure();
73+
$this
74+
->setName('encryption:migrate-key-storage-format')
75+
->setDescription('Migrate the format of the keystorage to a newer format');
76+
}
77+
78+
protected function execute(InputInterface $input, OutputInterface $output): int {
79+
$root = $this->util->getKeyStorageRoot();
80+
81+
$output->writeln("Updating key storage format");
82+
$this->updateKeys($root, $output);
83+
$output->writeln("Key storage format successfully updated");
84+
85+
return 0;
86+
}
87+
88+
/**
89+
* move keys to new key storage root
90+
*
91+
* @param string $root
92+
* @param OutputInterface $output
93+
* @return bool
94+
* @throws \Exception
95+
*/
96+
protected function updateKeys(string $root, OutputInterface $output) {
97+
$output->writeln("Start to update the keys:");
98+
99+
$this->updateSystemKeys($root);
100+
$this->updateUsersKeys($root, $output);
101+
$this->config->deleteSystemValue('encryption.key_storage_migrated');
102+
return true;
103+
}
104+
105+
/**
106+
* move system key folder
107+
*
108+
* @param string $root
109+
*/
110+
protected function updateSystemKeys($root) {
111+
if (!$this->rootView->is_dir($root . '/files_encryption')) {
112+
return;
113+
}
114+
115+
$this->traverseKeys($root . '/files_encryption', null);
116+
}
117+
118+
private function traverseKeys(string $folder, ?string $uid) {
119+
$listing = $this->rootView->getDirectoryContent($folder);
120+
121+
foreach ($listing as $node) {
122+
if ($node['mimetype'] === 'httpd/unix-directory') {
123+
//ignore
124+
} else {
125+
$endsWith = function ($haystack, $needle) {
126+
$length = strlen($needle);
127+
if ($length === 0) {
128+
return true;
129+
}
130+
131+
return (substr($haystack, -$length) === $needle);
132+
};
133+
134+
if ($node['name'] === 'fileKey' ||
135+
$endsWith($node['name'], '.privateKey') ||
136+
$endsWith($node['name'], '.publicKey') ||
137+
$endsWith($node['name'], '.shareKey')) {
138+
$path = $folder . '/' . $node['name'];
139+
140+
$content = $this->rootView->file_get_contents($path);
141+
142+
try {
143+
$this->crypto->decrypt($content);
144+
continue;
145+
} catch (\Exception $e) {
146+
// Ignore we now update the data.
147+
}
148+
149+
$data = [
150+
'key' => base64_encode($content),
151+
'uid' => $uid,
152+
];
153+
154+
$enc = $this->crypto->encrypt(json_encode($data));
155+
$this->rootView->file_put_contents($path, $enc);
156+
}
157+
}
158+
}
159+
}
160+
161+
private function traverseFileKeys(string $folder) {
162+
$listing = $this->rootView->getDirectoryContent($folder);
163+
164+
foreach ($listing as $node) {
165+
if ($node['mimetype'] === 'httpd/unix-directory') {
166+
$this->traverseFileKeys($folder . '/' . $node['name']);
167+
} else {
168+
$endsWith = function ($haystack, $needle) {
169+
$length = strlen($needle);
170+
if ($length === 0) {
171+
return true;
172+
}
173+
174+
return (substr($haystack, -$length) === $needle);
175+
};
176+
177+
if ($node['name'] === 'fileKey' ||
178+
$endsWith($node['name'], '.privateKey') ||
179+
$endsWith($node['name'], '.publicKey') ||
180+
$endsWith($node['name'], '.shareKey')) {
181+
$path = $folder . '/' . $node['name'];
182+
183+
$content = $this->rootView->file_get_contents($path);
184+
185+
try {
186+
$this->crypto->decrypt($content);
187+
continue;
188+
} catch (\Exception $e) {
189+
// Ignore we now update the data.
190+
}
191+
192+
$data = [
193+
'key' => base64_encode($content)
194+
];
195+
196+
$enc = $this->crypto->encrypt(json_encode($data));
197+
$this->rootView->file_put_contents($path, $enc);
198+
}
199+
}
200+
}
201+
}
202+
203+
204+
/**
205+
* setup file system for the given user
206+
*
207+
* @param string $uid
208+
*/
209+
protected function setupUserFS($uid) {
210+
\OC_Util::tearDownFS();
211+
\OC_Util::setupFS($uid);
212+
}
213+
214+
215+
/**
216+
* iterate over each user and move the keys to the new storage
217+
*
218+
* @param string $root
219+
* @param OutputInterface $output
220+
*/
221+
protected function updateUsersKeys(string $root, OutputInterface $output) {
222+
$progress = new ProgressBar($output);
223+
$progress->start();
224+
225+
foreach ($this->userManager->getBackends() as $backend) {
226+
$limit = 500;
227+
$offset = 0;
228+
do {
229+
$users = $backend->getUsers('', $limit, $offset);
230+
foreach ($users as $user) {
231+
$progress->advance();
232+
$this->setupUserFS($user);
233+
$this->updateUserKeys($root, $user);
234+
}
235+
$offset += $limit;
236+
} while (count($users) >= $limit);
237+
}
238+
$progress->finish();
239+
}
240+
241+
/**
242+
* move user encryption folder to new root folder
243+
*
244+
* @param string $root
245+
* @param string $user
246+
* @throws \Exception
247+
*/
248+
protected function updateUserKeys(string $root, string $user) {
249+
if ($this->userManager->userExists($user)) {
250+
$source = $root . '/' . $user . '/files_encryption/OC_DEFAULT_MODULE';
251+
if ($this->rootView->is_dir($source)) {
252+
$this->traverseKeys($source, $user);
253+
}
254+
255+
$source = $root . '/' . $user . '/files_encryption/keys';
256+
if ($this->rootView->is_dir($source)) {
257+
$this->traverseFileKeys($source);
258+
}
259+
}
260+
}
261+
}

core/register_command.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,14 @@
139139
)
140140
);
141141
$application->add(new OC\Core\Command\Encryption\ShowKeyStorageRoot($util));
142+
$application->add(new OC\Core\Command\Encryption\MigrateKeyStorage(
143+
$view,
144+
\OC::$server->getUserManager(),
145+
\OC::$server->getConfig(),
146+
$util,
147+
\OC::$server->getCrypto()
148+
)
149+
);
142150

143151
$application->add(new OC\Core\Command\Maintenance\DataFingerprint(\OC::$server->getConfig(), new \OC\AppFramework\Utility\TimeFactory()));
144152
$application->add(new OC\Core\Command\Maintenance\Mimetype\UpdateDB(\OC::$server->getMimeTypeDetector(), \OC::$server->getMimeTypeLoader()));

lib/composer/composer/autoload_classmap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,7 @@
808808
'OC\\Core\\Command\\Encryption\\Enable' => $baseDir . '/core/Command/Encryption/Enable.php',
809809
'OC\\Core\\Command\\Encryption\\EncryptAll' => $baseDir . '/core/Command/Encryption/EncryptAll.php',
810810
'OC\\Core\\Command\\Encryption\\ListModules' => $baseDir . '/core/Command/Encryption/ListModules.php',
811+
'OC\\Core\\Command\\Encryption\\MigrateKeyStorage' => $baseDir . '/core/Command/Encryption/MigrateKeyStorage.php',
811812
'OC\\Core\\Command\\Encryption\\SetDefaultModule' => $baseDir . '/core/Command/Encryption/SetDefaultModule.php',
812813
'OC\\Core\\Command\\Encryption\\ShowKeyStorageRoot' => $baseDir . '/core/Command/Encryption/ShowKeyStorageRoot.php',
813814
'OC\\Core\\Command\\Encryption\\Status' => $baseDir . '/core/Command/Encryption/Status.php',
@@ -1248,6 +1249,7 @@
12481249
'OC\\Repair\\NC16\\ClearCollectionsAccessCache' => $baseDir . '/lib/private/Repair/NC16/ClearCollectionsAccessCache.php',
12491250
'OC\\Repair\\NC18\\ResetGeneratedAvatarFlag' => $baseDir . '/lib/private/Repair/NC18/ResetGeneratedAvatarFlag.php',
12501251
'OC\\Repair\\NC20\\EncryptionLegacyCipher' => $baseDir . '/lib/private/Repair/NC20/EncryptionLegacyCipher.php',
1252+
'OC\\Repair\\NC20\\EncryptionMigration' => $baseDir . '/lib/private/Repair/NC20/EncryptionMigration.php',
12511253
'OC\\Repair\\OldGroupMembershipShares' => $baseDir . '/lib/private/Repair/OldGroupMembershipShares.php',
12521254
'OC\\Repair\\Owncloud\\DropAccountTermsTable' => $baseDir . '/lib/private/Repair/Owncloud/DropAccountTermsTable.php',
12531255
'OC\\Repair\\Owncloud\\SaveAccountsTableData' => $baseDir . '/lib/private/Repair/Owncloud/SaveAccountsTableData.php',

lib/composer/composer/autoload_static.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
837837
'OC\\Core\\Command\\Encryption\\Enable' => __DIR__ . '/../../..' . '/core/Command/Encryption/Enable.php',
838838
'OC\\Core\\Command\\Encryption\\EncryptAll' => __DIR__ . '/../../..' . '/core/Command/Encryption/EncryptAll.php',
839839
'OC\\Core\\Command\\Encryption\\ListModules' => __DIR__ . '/../../..' . '/core/Command/Encryption/ListModules.php',
840+
'OC\\Core\\Command\\Encryption\\MigrateKeyStorage' => __DIR__ . '/../../..' . '/core/Command/Encryption/MigrateKeyStorage.php',
840841
'OC\\Core\\Command\\Encryption\\SetDefaultModule' => __DIR__ . '/../../..' . '/core/Command/Encryption/SetDefaultModule.php',
841842
'OC\\Core\\Command\\Encryption\\ShowKeyStorageRoot' => __DIR__ . '/../../..' . '/core/Command/Encryption/ShowKeyStorageRoot.php',
842843
'OC\\Core\\Command\\Encryption\\Status' => __DIR__ . '/../../..' . '/core/Command/Encryption/Status.php',
@@ -1277,6 +1278,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
12771278
'OC\\Repair\\NC16\\ClearCollectionsAccessCache' => __DIR__ . '/../../..' . '/lib/private/Repair/NC16/ClearCollectionsAccessCache.php',
12781279
'OC\\Repair\\NC18\\ResetGeneratedAvatarFlag' => __DIR__ . '/../../..' . '/lib/private/Repair/NC18/ResetGeneratedAvatarFlag.php',
12791280
'OC\\Repair\\NC20\\EncryptionLegacyCipher' => __DIR__ . '/../../..' . '/lib/private/Repair/NC20/EncryptionLegacyCipher.php',
1281+
'OC\\Repair\\NC20\\EncryptionMigration' => __DIR__ . '/../../..' . '/lib/private/Repair/NC20/EncryptionMigration.php',
12801282
'OC\\Repair\\OldGroupMembershipShares' => __DIR__ . '/../../..' . '/lib/private/Repair/OldGroupMembershipShares.php',
12811283
'OC\\Repair\\Owncloud\\DropAccountTermsTable' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/DropAccountTermsTable.php',
12821284
'OC\\Repair\\Owncloud\\SaveAccountsTableData' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/SaveAccountsTableData.php',

0 commit comments

Comments
 (0)