*
* @author Michael Gapczynski
* @copyright 2012 Michael Gapczynski mtgap@owncloud.com
+* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
+set_include_path(
+ get_include_path() . PATH_SEPARATOR .
+ \OC_App::getAppPath('files_external') . '/3rdparty/phpseclib/phpseclib'
+);
+include('Crypt/AES.php');
+
/**
* Class to configure the config/mount.php and data/$user/mount.php files
-*/
+ */
+// TODO: make this class non-static
class OC_Mount_Config {
const MOUNT_TYPE_GLOBAL = 'global';
// whether to skip backend test (for unit tests, as this static class is not mockable)
public static $skipTest = false;
+ // password encryption cipher
+ private static $cipher;
+
/**
* Get details on each of the external storage backends, used for the mount config UI
* If a custom UI is needed, add the key 'custom' and a javascript file with that name will be loaded
if (strpos($mount['class'], 'OC_Filestorage_') !== false) {
$mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15);
}
+ $mount['options'] = self::decryptPasswords($mount['options']);
// Remove '/$user/files/' from mount point
$mountPoint = substr($mountPoint, 13);
// Merge the mount point into the current mount points
if (strpos($mount['class'], 'OC_Filestorage_') !== false) {
$mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15);
}
+ $mount['options'] = self::decryptPasswords($mount['options']);
// Remove '/$user/files/' from mount point
$mountPoint = substr($mountPoint, 13);
// Merge the mount point into the current mount points
if (strpos($mount['class'], 'OC_Filestorage_') !== false) {
$mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15);
}
+ $mount['options'] = self::decryptPasswords($mount['options']);
// Remove '/uid/files/' from mount point
$personal[substr($mountPoint, strlen($uid) + 8)] = array(
'class' => $mount['class'],
} else {
$mountPoint = '/$user/files/'.ltrim($mountPoint, '/');
}
- $mount = array($applicable => array($mountPoint => array('class' => $class, 'options' => $classOptions)));
+
+ $mount = array($applicable => array(
+ $mountPoint => array(
+ 'class' => $class,
+ 'options' => self::encryptPasswords($classOptions))
+ )
+ );
$mountPoints = self::readData($isPersonal);
// Merge the new mount point into the current mount points
if (isset($mountPoints[$mountType])) {
return $txt;
}
+
+ /**
+ * Encrypt passwords in the given config options
+ * @param array $options mount options
+ * @return array updated options
+ */
+ private static function encryptPasswords($options) {
+ if (isset($options['password'])) {
+ $options['password_encrypted'] = base64_encode(self::getCipher()->encrypt($options['password']));
+ unset($options['password']);
+ }
+ return $options;
+ }
+
+ /**
+ * Decrypt passwords in the given config options
+ * @param array $options mount options
+ * @return array updated options
+ */
+ private static function decryptPasswords($options) {
+ // note: legacy options might still have the unencrypted password in the "password" field
+ if (isset($options['password_encrypted'])) {
+ $options['password'] = self::getCipher()->decrypt(base64_decode($options['password_encrypted']));
+ unset($options['password_encrypted']);
+ }
+ return $options;
+ }
+
+ /**
+ * Returns the encryption cipher
+ */
+ private static function getCipher() {
+ if (!isset(self::$cipher)) {
+ self::$cipher = new Crypt_AES(CRYPT_AES_MODE_CBC);
+ self::$cipher->setKey(\OCP\Config::getSystemValue('passwordsalt'));
+ }
+ return self::$cipher;
+ }
}
return json_decode(file_get_contents($configFile), true);
}
+ /**
+ * Write the user config, to simulate existing files
+ */
+ private function writeUserConfig($config) {
+ $configFile = $this->userHome . '/mount.json';
+ file_put_contents($configFile, json_encode($config));
+ }
+
/**
* Test mount point validation
*/
$savedMountConfig = $config['ext']['configuration'];
$this->assertEquals($mountConfig, $savedMountConfig);
}
+
+ /**
+ * Test password obfuscation
+ */
+ public function testPasswordObfuscation() {
+ $mountType = OC_Mount_Config::MOUNT_TYPE_USER;
+ $applicable = 'test';
+ $isPersonal = true;
+ $mountConfig = array(
+ 'host' => 'smbhost',
+ 'user' => 'smbuser',
+ 'password' => 'smbpassword',
+ 'share' => 'smbshare',
+ 'root' => 'smbroot'
+ );
+
+ // write config
+ $this->assertTrue(
+ OC_Mount_Config::addMountPoint(
+ '/ext',
+ '\OC\Files\Storage\SMB',
+ $mountConfig,
+ $mountType,
+ $applicable,
+ $isPersonal
+ )
+ );
+
+ // note: password re-reading is covered by testReadWritePersonalConfig
+
+ // check that password inside the file is NOT in plain text
+ $config = $this->readUserConfig();
+ $savedConfig = $config[$mountType][$applicable]['/test/files/ext']['options'];
+
+ // no more clear text password in file
+ $this->assertFalse(isset($savedConfig['password']));
+
+ // encrypted password is present
+ $this->assertNotEquals($mountConfig['password'], $savedConfig['password_encrypted']);
+ }
+
+ /**
+ * Test read legacy passwords
+ */
+ public function testReadLegacyPassword() {
+ $mountType = OC_Mount_Config::MOUNT_TYPE_USER;
+ $applicable = 'test';
+ $isPersonal = true;
+ $mountConfig = array(
+ 'host' => 'smbhost',
+ 'user' => 'smbuser',
+ 'password' => 'smbpassword',
+ 'share' => 'smbshare',
+ 'root' => 'smbroot'
+ );
+
+ // write config
+ $this->assertTrue(
+ OC_Mount_Config::addMountPoint(
+ '/ext',
+ '\OC\Files\Storage\SMB',
+ $mountConfig,
+ $mountType,
+ $applicable,
+ $isPersonal
+ )
+ );
+
+ $config = $this->readUserConfig();
+ // simulate non-encrypted password situation
+ $config[$mountType][$applicable]['/test/files/ext']['options']['password'] = 'smbpasswd';
+
+ $this->writeUserConfig($config);
+
+ // re-read config, password was read correctly
+ $config = OC_Mount_Config::getPersonalMountPoints();
+ $savedMountConfig = $config['ext']['configuration'];
+ $this->assertEquals($mountConfig, $savedMountConfig);
+ }
}