2012-07-11 16:51:27 +00:00
< ? php
/**
* ownCloud
*
* @ author Sam Tuke , Frank Karlitschek
2013-05-19 23:24:36 +00:00
* @ copyright 2012 Sam Tuke < samtuke @ owncloud . com > ,
2013-05-09 12:43:06 +00:00
* Frank Karlitschek < frank @ owncloud . org >
2012-07-11 16:51:27 +00:00
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation ; either
* version 3 of the License , or any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details .
*
* You should have received a copy of the GNU Affero General Public
* License along with this library . If not , see < http :// www . gnu . org / licenses />.
*
*/
2013-02-26 18:11:29 +00:00
# Bugs
# ----
# Sharing a file to a user without encryption set up will not provide them with access but won't notify the sharer
2013-04-10 15:37:03 +00:00
# Sharing all files to admin for recovery purposes still in progress
# Possibly public links are broken (not tested since last merge of master)
2013-02-26 18:11:29 +00:00
# Missing features
# ----------------
# Make sure user knows if large files weren't encrypted
2013-03-20 18:26:59 +00:00
# Test
# ----
# Test that writing files works when recovery is enabled, and sharing API is disabled
# Test trashbin support
2013-02-26 18:11:29 +00:00
// Old Todo:
2012-07-11 16:51:27 +00:00
// - Crypt/decrypt button in the userinterface
// - Setting if crypto should be on by default
2013-01-24 18:37:34 +00:00
// - Add a setting "Don´ t encrypt files larger than xx because of performance
// reasons"
2012-07-11 16:51:27 +00:00
2012-10-17 15:35:19 +00:00
namespace OCA\Encryption ;
2012-07-11 16:51:27 +00:00
/**
2012-07-25 14:33:25 +00:00
* @ brief Class for utilities relating to encrypted file storage system
2013-05-27 10:41:55 +00:00
* @ param \OC_FilesystemView $view expected to have OC '/' as root path
2013-01-31 19:40:51 +00:00
* @ param string $userId ID of the logged in user
* @ param int $client indicating status of client side encryption . Currently
2012-07-25 14:33:25 +00:00
* unused , likely to become obsolete shortly
2012-07-11 16:51:27 +00:00
*/
2013-05-27 10:41:55 +00:00
class Util {
2013-05-19 23:24:36 +00:00
2013-01-24 18:37:34 +00:00
// Web UI:
2013-05-19 23:24:36 +00:00
2013-01-24 18:37:34 +00:00
//// DONE: files created via web ui are encrypted
//// DONE: file created & encrypted via web ui are readable in web ui
//// DONE: file created & encrypted via web ui are readable via webdav
2013-05-19 23:24:36 +00:00
2013-01-24 18:37:34 +00:00
// WebDAV:
2013-05-19 23:24:36 +00:00
2013-01-24 18:37:34 +00:00
//// DONE: new data filled files added via webdav get encrypted
//// DONE: new data filled files added via webdav are readable via webdav
//// DONE: reading unencrypted files when encryption is enabled works via
//// webdav
//// DONE: files created & encrypted via web ui are readable via webdav
2013-05-19 23:24:36 +00:00
2013-01-24 18:37:34 +00:00
// Legacy support:
2013-05-19 23:24:36 +00:00
2013-01-24 18:37:34 +00:00
//// DONE: add method to check if file is encrypted using new system
//// DONE: add method to check if file is encrypted using old system
//// DONE: add method to fetch legacy key
//// DONE: add method to decrypt legacy encrypted data
2013-05-19 23:24:36 +00:00
2013-01-24 18:37:34 +00:00
// Admin UI:
2013-05-19 23:24:36 +00:00
2013-01-24 18:37:34 +00:00
//// DONE: changing user password also changes encryption passphrase
2013-05-19 23:24:36 +00:00
2013-01-24 18:37:34 +00:00
//// TODO: add support for optional recovery in case of lost passphrase / keys
//// TODO: add admin optional required long passphrase for users
//// TODO: implement flag system to allow user to specify encryption by folder, subfolder, etc.
2013-05-19 23:24:36 +00:00
2013-01-24 18:37:34 +00:00
// Integration testing:
2013-05-19 23:24:36 +00:00
2013-01-24 18:37:34 +00:00
//// TODO: test new encryption with versioning
2013-05-10 22:23:30 +00:00
//// DONE: test new encryption with sharing
2013-01-24 18:37:34 +00:00
//// TODO: test new encryption with proxies
2013-05-19 23:24:36 +00:00
2012-07-11 16:51:27 +00:00
private $view ; // OC_FilesystemView object for filesystem operations
2013-01-23 19:24:26 +00:00
private $userId ; // ID of the currently logged-in user
2012-07-11 16:51:27 +00:00
private $pwd ; // User Password
private $client ; // Client side encryption mode flag
2013-01-14 15:39:04 +00:00
private $publicKeyDir ; // Dir containing all public user keys
private $encryptionDir ; // Dir containing user's files_encryption
private $keyfilesPath ; // Dir containing user's keyfiles
private $shareKeysPath ; // Dir containing env keys for shared files
2012-11-16 18:31:37 +00:00
private $publicKeyPath ; // Path to user's public key
private $privateKeyPath ; // Path to user's private key
2013-05-13 13:15:35 +00:00
private $publicShareKeyId ;
2013-05-13 15:40:57 +00:00
private $recoveryKeyId ;
2013-05-19 23:24:36 +00:00
private $isPublic ;
2012-07-11 16:51:27 +00:00
2013-05-19 23:24:36 +00:00
/**
* @ param \OC_FilesystemView $view
* @ param $userId
* @ param bool $client
*/
2013-05-27 10:41:55 +00:00
public function __construct ( \OC_FilesystemView $view , $userId , $client = false ) {
2013-05-13 19:24:59 +00:00
2012-07-11 16:51:27 +00:00
$this -> view = $view ;
2012-07-25 14:33:25 +00:00
$this -> userId = $userId ;
2012-07-11 16:51:27 +00:00
$this -> client = $client ;
2013-05-19 23:24:36 +00:00
$this -> isPublic = false ;
2013-05-27 10:41:55 +00:00
$this -> publicShareKeyId = \OC_Appconfig :: getValue ( 'files_encryption' , 'publicShareKeyId' );
$this -> recoveryKeyId = \OC_Appconfig :: getValue ( 'files_encryption' , 'recoveryKeyId' );
2013-05-19 23:24:36 +00:00
// if we are anonymous/public
2013-05-28 07:27:04 +00:00
if ( \OCA\Encryption\Helper :: isPublicAccess ()) {
2013-05-19 23:24:36 +00:00
$this -> userId = $this -> publicShareKeyId ;
// only handle for files_sharing app
2013-05-28 07:36:14 +00:00
if ( isset ( $GLOBALS [ 'app' ]) && $GLOBALS [ 'app' ] === 'files_sharing' ) {
2013-05-19 23:24:36 +00:00
$this -> userDir = '/' . $GLOBALS [ 'fileOwner' ];
$this -> fileFolderName = 'files' ;
2013-05-27 10:41:55 +00:00
$this -> userFilesDir = '/' . $GLOBALS [ 'fileOwner' ] . '/'
. $this -> fileFolderName ; // TODO: Does this need to be user configurable?
2013-05-19 23:24:36 +00:00
$this -> publicKeyDir = '/' . 'public-keys' ;
$this -> encryptionDir = '/' . $GLOBALS [ 'fileOwner' ] . '/' . 'files_encryption' ;
$this -> keyfilesPath = $this -> encryptionDir . '/' . 'keyfiles' ;
$this -> shareKeysPath = $this -> encryptionDir . '/' . 'share-keys' ;
2013-05-27 10:41:55 +00:00
$this -> publicKeyPath =
$this -> publicKeyDir . '/' . $this -> userId . '.public.key' ; // e.g. data/public-keys/admin.public.key
$this -> privateKeyPath =
'/owncloud_private_key/' . $this -> userId . '.private.key' ; // e.g. data/admin/admin.private.key
2013-05-19 23:24:36 +00:00
$this -> isPublic = true ;
}
} else {
$this -> userDir = '/' . $this -> userId ;
$this -> fileFolderName = 'files' ;
2013-05-27 10:41:55 +00:00
$this -> userFilesDir =
'/' . $this -> userId . '/' . $this -> fileFolderName ; // TODO: Does this need to be user configurable?
2013-05-19 23:24:36 +00:00
$this -> publicKeyDir = '/' . 'public-keys' ;
$this -> encryptionDir = '/' . $this -> userId . '/' . 'files_encryption' ;
$this -> keyfilesPath = $this -> encryptionDir . '/' . 'keyfiles' ;
$this -> shareKeysPath = $this -> encryptionDir . '/' . 'share-keys' ;
2013-05-27 10:41:55 +00:00
$this -> publicKeyPath =
$this -> publicKeyDir . '/' . $this -> userId . '.public.key' ; // e.g. data/public-keys/admin.public.key
$this -> privateKeyPath =
$this -> encryptionDir . '/' . $this -> userId . '.private.key' ; // e.g. data/admin/admin.private.key
2013-05-19 23:24:36 +00:00
}
2012-07-11 16:51:27 +00:00
}
2013-05-19 23:24:36 +00:00
/**
* @ return bool
*/
2013-05-23 21:56:31 +00:00
public function ready () {
2013-05-19 23:24:36 +00:00
if (
2013-05-27 10:41:55 +00:00
! $this -> view -> file_exists ( $this -> encryptionDir )
or ! $this -> view -> file_exists ( $this -> keyfilesPath )
or ! $this -> view -> file_exists ( $this -> shareKeysPath )
or ! $this -> view -> file_exists ( $this -> publicKeyPath )
or ! $this -> view -> file_exists ( $this -> privateKeyPath )
2012-07-11 16:51:27 +00:00
) {
2013-05-19 23:24:36 +00:00
2012-07-11 16:51:27 +00:00
return false ;
2013-05-19 23:24:36 +00:00
2012-07-11 16:51:27 +00:00
} else {
2013-05-19 23:24:36 +00:00
2012-07-11 16:51:27 +00:00
return true ;
2013-05-19 23:24:36 +00:00
2012-07-11 16:51:27 +00:00
}
2013-05-19 23:24:36 +00:00
2012-07-11 16:51:27 +00:00
}
2013-05-19 23:24:36 +00:00
/**
* @ brief Sets up user folders and keys for serverside encryption
2013-05-29 23:13:22 +00:00
*
* @ param string $passphrase to encrypt server - stored private key with
* @ return bool
2013-05-19 23:24:36 +00:00
*/
2013-05-27 10:41:55 +00:00
public function setupServerSide ( $passphrase = null ) {
2013-05-19 23:24:36 +00:00
2013-03-19 18:53:15 +00:00
// Set directories to check / create
2013-05-19 23:24:36 +00:00
$setUpDirs = array (
2013-05-27 10:41:55 +00:00
$this -> userDir ,
$this -> userFilesDir ,
$this -> publicKeyDir ,
$this -> encryptionDir ,
$this -> keyfilesPath ,
$this -> shareKeysPath
2013-03-19 18:53:15 +00:00
);
2013-05-19 23:24:36 +00:00
2013-03-19 18:53:15 +00:00
// Check / create all necessary dirs
2013-05-27 10:41:55 +00:00
foreach ( $setUpDirs as $dirPath ) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
if ( ! $this -> view -> file_exists ( $dirPath )) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$this -> view -> mkdir ( $dirPath );
2013-05-19 23:24:36 +00:00
2013-03-19 18:53:15 +00:00
}
2013-05-19 23:24:36 +00:00
2013-01-14 15:39:04 +00:00
}
2013-05-19 23:24:36 +00:00
2012-07-11 16:51:27 +00:00
// Create user keypair
2013-05-20 19:22:03 +00:00
// we should never override a keyfile
2013-05-19 23:24:36 +00:00
if (
2013-05-27 10:41:55 +00:00
! $this -> view -> file_exists ( $this -> publicKeyPath )
&& ! $this -> view -> file_exists ( $this -> privateKeyPath )
2012-07-11 16:51:27 +00:00
) {
2013-05-19 23:24:36 +00:00
2012-07-11 16:51:27 +00:00
// Generate keypair
$keypair = Crypt :: createKeypair ();
2013-05-19 23:24:36 +00:00
2012-07-25 14:33:25 +00:00
\OC_FileProxy :: $enabled = false ;
2013-05-19 23:24:36 +00:00
2012-07-11 16:51:27 +00:00
// Save public key
2013-05-27 10:41:55 +00:00
$this -> view -> file_put_contents ( $this -> publicKeyPath , $keypair [ 'publicKey' ]);
2013-05-19 23:24:36 +00:00
2012-07-25 14:33:25 +00:00
// Encrypt private key with user pwd as passphrase
2013-05-27 10:41:55 +00:00
$encryptedPrivateKey = Crypt :: symmetricEncryptFileContent ( $keypair [ 'privateKey' ], $passphrase );
2013-05-19 23:24:36 +00:00
2012-07-25 14:33:25 +00:00
// Save private key
2013-05-27 10:41:55 +00:00
$this -> view -> file_put_contents ( $this -> privateKeyPath , $encryptedPrivateKey );
2013-05-19 23:24:36 +00:00
2012-07-25 14:33:25 +00:00
\OC_FileProxy :: $enabled = true ;
2013-05-19 23:24:36 +00:00
2013-05-20 19:22:03 +00:00
} else {
// check if public-key exists but private-key is missing
2013-05-27 10:41:55 +00:00
if ( $this -> view -> file_exists ( $this -> publicKeyPath ) && ! $this -> view -> file_exists ( $this -> privateKeyPath )) {
\OCP\Util :: writeLog ( 'Encryption library' ,
'public key exists but private key is missing for "' . $this -> userId . '"' , \OCP\Util :: FATAL );
2013-05-20 19:22:03 +00:00
return false ;
2013-05-27 10:41:55 +00:00
} else {
if ( ! $this -> view -> file_exists ( $this -> publicKeyPath ) && $this -> view -> file_exists ( $this -> privateKeyPath )
) {
\OCP\Util :: writeLog ( 'Encryption library' ,
'private key exists but public key is missing for "' . $this -> userId . '"' , \OCP\Util :: FATAL );
return false ;
}
2013-05-20 19:22:03 +00:00
}
2013-05-01 17:18:31 +00:00
}
2013-05-19 23:24:36 +00:00
2013-05-01 17:18:31 +00:00
// If there's no record for this user's encryption preferences
2013-05-27 10:41:55 +00:00
if ( false === $this -> recoveryEnabledForUser ()) {
2013-05-19 23:24:36 +00:00
2013-05-01 17:18:31 +00:00
// create database configuration
2013-05-19 05:04:31 +00:00
$sql = 'INSERT INTO `*PREFIX*encryption` (`uid`,`mode`,`recovery_enabled`) VALUES (?,?,?)' ;
2013-05-27 10:41:55 +00:00
$args = array (
$this -> userId ,
'server-side' ,
0
);
$query = \OCP\DB :: prepare ( $sql );
$query -> execute ( $args );
2013-05-19 23:24:36 +00:00
2012-07-11 16:51:27 +00:00
}
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
return true ;
2013-05-19 23:24:36 +00:00
2012-07-11 16:51:27 +00:00
}
2013-05-13 13:15:35 +00:00
2013-05-19 23:24:36 +00:00
/**
* @ return string
*/
2013-05-23 21:56:31 +00:00
public function getPublicShareKeyId () {
2013-05-13 13:15:35 +00:00
return $this -> publicShareKeyId ;
}
2013-05-19 23:24:36 +00:00
2013-03-20 18:26:59 +00:00
/**
* @ brief Check whether pwd recovery is enabled for a given user
2013-05-19 23:24:36 +00:00
* @ return bool 1 = yes , 0 = no , false = no record
*
* @ note If records are not being returned , check for a hidden space
2013-03-20 18:26:59 +00:00
* at the start of the uid in db
*/
2013-05-23 21:56:31 +00:00
public function recoveryEnabledForUser () {
2013-05-19 23:24:36 +00:00
2013-06-10 07:53:29 +00:00
$sql = 'SELECT `recovery_enabled` FROM `*PREFIX*encryption` WHERE `uid` = ?' ;
2013-03-19 18:53:15 +00:00
2013-05-27 10:41:55 +00:00
$args = array ( $this -> userId );
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$query = \OCP\DB :: prepare ( $sql );
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$result = $query -> execute ( $args );
2013-05-19 23:24:36 +00:00
2013-05-01 17:18:31 +00:00
$recoveryEnabled = array ();
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
if ( \OCP\DB :: isError ( $result )) {
\OCP\Util :: writeLog ( 'Encryption library' , \OC_DB :: getErrorMessage ( $result ), \OCP\Util :: ERROR );
2013-05-25 21:07:19 +00:00
} else {
2013-05-27 10:41:55 +00:00
if ( $result -> numRows () > 0 ) {
$row = $result -> fetchRow ();
if ( isset ( $row [ 'recovery_enabled' ])) {
$recoveryEnabled [] = $row [ 'recovery_enabled' ];
}
2013-05-25 21:07:19 +00:00
}
2013-03-20 18:26:59 +00:00
}
2013-05-19 23:24:36 +00:00
2013-05-01 17:18:31 +00:00
// If no record is found
2013-05-27 10:41:55 +00:00
if ( empty ( $recoveryEnabled )) {
2013-05-19 23:24:36 +00:00
2013-05-01 17:18:31 +00:00
return false ;
2013-05-19 23:24:36 +00:00
// If a record is found
2013-05-01 17:18:31 +00:00
} else {
2013-05-19 23:24:36 +00:00
2013-05-01 17:18:31 +00:00
return $recoveryEnabled [ 0 ];
2013-05-19 23:24:36 +00:00
2013-05-01 17:18:31 +00:00
}
2013-05-19 23:24:36 +00:00
2013-03-20 18:26:59 +00:00
}
2013-05-19 23:24:36 +00:00
2013-03-20 18:26:59 +00:00
/**
* @ brief Enable / disable pwd recovery for a given user
* @ param bool $enabled Whether to enable or disable recovery
* @ return bool
*/
2013-05-27 10:41:55 +00:00
public function setRecoveryForUser ( $enabled ) {
2013-05-19 23:24:36 +00:00
2013-05-01 17:18:31 +00:00
$recoveryStatus = $this -> recoveryEnabledForUser ();
2013-05-19 23:24:36 +00:00
2013-05-01 17:18:31 +00:00
// If a record for this user already exists, update it
2013-05-27 10:41:55 +00:00
if ( false === $recoveryStatus ) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$sql = 'INSERT INTO `*PREFIX*encryption` (`uid`,`mode`,`recovery_enabled`) VALUES (?,?,?)' ;
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$args = array (
$this -> userId ,
'server-side' ,
$enabled
);
2013-05-19 23:24:36 +00:00
// Create a new record instead
2013-05-01 17:18:31 +00:00
} else {
2013-05-19 23:24:36 +00:00
2013-06-10 07:53:29 +00:00
$sql = 'UPDATE `*PREFIX*encryption` SET `recovery_enabled` = ? WHERE `uid` = ?' ;
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$args = array (
$enabled ,
$this -> userId
);
2013-05-19 23:24:36 +00:00
2013-05-01 17:18:31 +00:00
}
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$query = \OCP\DB :: prepare ( $sql );
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
if ( $query -> execute ( $args )) {
2013-05-19 23:24:36 +00:00
2013-03-20 18:26:59 +00:00
return true ;
2013-05-19 23:24:36 +00:00
2013-03-20 18:26:59 +00:00
} else {
2013-05-19 23:24:36 +00:00
2013-03-20 18:26:59 +00:00
return false ;
2013-05-19 23:24:36 +00:00
2013-03-20 18:26:59 +00:00
}
2013-05-19 23:24:36 +00:00
2013-03-19 18:53:15 +00:00
}
2013-05-19 23:24:36 +00:00
2013-01-23 19:24:26 +00:00
/**
* @ brief Find all files and their encryption status within a directory
* @ param string $directory The path of the parent directory to search
2013-05-27 10:41:55 +00:00
* @ param bool $found the founded files if called again
2013-01-23 19:24:26 +00:00
* @ return mixed false if 0 found , array on success . Keys : name , path
2013-01-29 19:54:40 +00:00
* @ note $directory needs to be a path relative to OC data dir . e . g .
* / admin / files NOT / backup OR / home / www / oc / data / admin / files
2013-01-23 19:24:26 +00:00
*/
2013-05-27 10:41:55 +00:00
public function findEncFiles ( $directory , & $found = false ) {
2013-05-19 23:24:36 +00:00
2013-01-23 19:24:26 +00:00
// Disable proxy - we don't want files to be decrypted before
// we handle them
\OC_FileProxy :: $enabled = false ;
2013-05-19 23:24:36 +00:00
2013-05-27 18:44:38 +00:00
if ( $found === false ) {
2013-05-27 10:41:55 +00:00
$found = array (
'plain' => array (),
'encrypted' => array (),
'legacy' => array ()
);
2013-05-21 22:53:07 +00:00
}
2013-05-19 23:24:36 +00:00
if (
2013-05-27 10:41:55 +00:00
$this -> view -> is_dir ( $directory )
&& $handle = $this -> view -> opendir ( $directory )
2013-01-23 19:24:26 +00:00
) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
while ( false !== ( $file = readdir ( $handle ))) {
2013-05-19 23:24:36 +00:00
2012-07-31 18:28:11 +00:00
if (
2013-05-27 18:44:38 +00:00
$file !== " . "
&& $file !== " .. "
2012-07-31 18:28:11 +00:00
) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$filePath = $directory . '/' . $this -> view -> getRelativePath ( '/' . $file );
2013-05-29 23:13:22 +00:00
$relPath = \OCA\Encryption\Helper :: stripUserFilesPath ( $filePath );
2013-05-19 23:24:36 +00:00
2013-01-23 19:24:26 +00:00
// If the path is a directory, search
// its contents
2013-05-27 10:41:55 +00:00
if ( $this -> view -> is_dir ( $filePath )) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$this -> findEncFiles ( $filePath , $found );
2013-05-19 23:24:36 +00:00
// If the path is a file, determine
// its encryption status
2013-05-27 10:41:55 +00:00
} elseif ( $this -> view -> is_file ( $filePath )) {
2013-05-19 23:24:36 +00:00
2013-01-23 19:24:26 +00:00
// Disable proxies again, some-
2013-01-31 19:40:51 +00:00
// where they got re-enabled :/
2013-01-23 19:24:26 +00:00
\OC_FileProxy :: $enabled = false ;
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$data = $this -> view -> file_get_contents ( $filePath );
2013-05-19 23:24:36 +00:00
2013-01-23 19:24:26 +00:00
// If the file is encrypted
2013-01-31 19:40:51 +00:00
// NOTE: If the userId is
// empty or not set, file will
// detected as plain
2013-02-01 19:31:15 +00:00
// NOTE: This is inefficient;
// scanning every file like this
// will eat server resources :(
2013-05-19 23:24:36 +00:00
if (
2013-05-27 10:41:55 +00:00
Keymanager :: getFileKey ( $this -> view , $this -> userId , $relPath )
&& Crypt :: isCatfileContent ( $data )
2013-01-31 19:40:51 +00:00
) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$found [ 'encrypted' ][] = array (
'name' => $file ,
'path' => $filePath
);
2013-05-19 23:24:36 +00:00
// If the file uses old
// encryption system
2013-05-24 15:35:00 +00:00
} elseif ( Crypt :: isLegacyEncryptedContent ( $data , $relPath ) ) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$found [ 'legacy' ][] = array (
'name' => $file ,
'path' => $filePath
);
2013-05-19 23:24:36 +00:00
// If the file is not encrypted
2013-01-23 19:24:26 +00:00
} else {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$found [ 'plain' ][] = array (
'name' => $file ,
'path' => $relPath
);
2013-05-19 23:24:36 +00:00
2012-07-31 18:28:11 +00:00
}
2013-05-19 23:24:36 +00:00
2012-07-31 18:28:11 +00:00
}
2013-05-19 23:24:36 +00:00
2012-07-31 18:28:11 +00:00
}
2013-05-19 23:24:36 +00:00
2012-07-31 18:28:11 +00:00
}
2013-05-19 23:24:36 +00:00
2013-01-23 19:24:26 +00:00
\OC_FileProxy :: $enabled = true ;
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
if ( empty ( $found )) {
2013-05-19 23:24:36 +00:00
2013-01-23 19:24:26 +00:00
return false ;
2013-05-19 23:24:36 +00:00
2012-07-31 18:28:11 +00:00
} else {
2013-05-19 23:24:36 +00:00
2013-01-23 19:24:26 +00:00
return $found ;
2013-05-19 23:24:36 +00:00
2012-07-31 18:28:11 +00:00
}
2013-05-19 23:24:36 +00:00
2012-07-31 18:28:11 +00:00
}
2013-05-19 23:24:36 +00:00
2013-01-23 19:24:26 +00:00
\OC_FileProxy :: $enabled = true ;
2013-05-19 23:24:36 +00:00
2012-07-31 18:28:11 +00:00
return false ;
}
2013-05-19 23:24:36 +00:00
/**
* @ brief Fetch the last lines of a file efficiently
* @ note Safe to use on large files ; does not read entire file to memory
* @ note Derivative of http :// tekkie . flashbit . net / php / tail - functionality - in - php
*/
2013-05-27 10:41:55 +00:00
public function tail ( $filename , $numLines ) {
2013-05-19 23:24:36 +00:00
2013-03-09 18:18:34 +00:00
\OC_FileProxy :: $enabled = false ;
2013-05-19 23:24:36 +00:00
2013-03-09 18:18:34 +00:00
$text = '' ;
$pos = - 1 ;
2013-05-27 10:41:55 +00:00
$handle = $this -> view -> fopen ( $filename , 'r' );
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
while ( $numLines > 0 ) {
2013-03-09 18:18:34 +00:00
-- $pos ;
2013-05-27 10:41:55 +00:00
if ( fseek ( $handle , $pos , SEEK_END ) !== 0 ) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
rewind ( $handle );
2013-03-09 18:18:34 +00:00
$numLines = 0 ;
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
} elseif ( fgetc ( $handle ) === " \n " ) {
2013-05-19 23:24:36 +00:00
2013-03-09 18:18:34 +00:00
-- $numLines ;
2013-05-19 23:24:36 +00:00
2013-03-09 18:18:34 +00:00
}
2013-05-27 10:41:55 +00:00
$block_size = ( - $pos ) % 8192 ;
if ( $block_size === 0 || $numLines === 0 ) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$text = fread ( $handle , ( $block_size === 0 ? 8192 : $block_size )) . $text ;
2013-05-19 23:24:36 +00:00
2013-03-09 18:18:34 +00:00
}
}
2013-05-27 10:41:55 +00:00
fclose ( $handle );
2013-05-19 23:24:36 +00:00
2013-03-09 18:18:34 +00:00
\OC_FileProxy :: $enabled = true ;
2013-05-19 23:24:36 +00:00
2013-03-09 18:18:34 +00:00
return $text ;
}
2013-05-19 23:24:36 +00:00
2013-05-09 16:16:59 +00:00
/**
2013-05-19 23:24:36 +00:00
* @ brief Check if a given path identifies an encrypted file
2013-05-29 23:13:22 +00:00
* @ param string $path
2013-05-19 23:24:36 +00:00
* @ return boolean
*/
2013-05-27 10:41:55 +00:00
public function isEncryptedPath ( $path ) {
2013-05-19 23:24:36 +00:00
2013-05-14 20:32:39 +00:00
// Disable encryption proxy so data retrieved is in its
2012-12-04 19:53:13 +00:00
// original form
2013-05-19 23:24:36 +00:00
$proxyStatus = \OC_FileProxy :: $enabled ;
2012-12-04 19:53:13 +00:00
\OC_FileProxy :: $enabled = false ;
2013-05-14 20:32:39 +00:00
2013-05-19 23:24:36 +00:00
// we only need 24 byte from the last chunk
$data = '' ;
2013-05-27 10:41:55 +00:00
$handle = $this -> view -> fopen ( $path , 'r' );
2013-05-28 18:50:14 +00:00
if ( is_resource ( $handle ) && ! fseek ( $handle , - 24 , SEEK_END )) {
2013-05-27 10:41:55 +00:00
$data = fgets ( $handle );
2013-05-19 23:24:36 +00:00
}
2013-05-14 20:32:39 +00:00
2013-05-19 23:24:36 +00:00
// re-enable proxy
2013-05-14 20:32:39 +00:00
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
return Crypt :: isCatfileContent ( $data );
2013-05-19 23:24:36 +00:00
2012-10-17 15:35:19 +00:00
}
2013-04-26 22:05:20 +00:00
2013-05-09 16:16:59 +00:00
/**
2013-05-19 23:24:36 +00:00
* @ brief get the file size of the unencrypted file
* @ param string $path absolute path
* @ return bool
*/
2013-05-27 10:41:55 +00:00
public function getFileSize ( $path ) {
2013-04-26 22:05:20 +00:00
2013-05-09 16:16:59 +00:00
$result = 0 ;
2013-04-26 22:05:20 +00:00
2013-05-09 16:16:59 +00:00
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy :: $enabled ;
\OC_FileProxy :: $enabled = false ;
2013-04-26 22:05:20 +00:00
2013-05-29 23:13:22 +00:00
// split the path parts
$pathParts = explode ( '/' , $path );
// get relative path
$relativePath = \OCA\Encryption\Helper :: stripUserFilesPath ( $path );
2013-04-26 22:05:20 +00:00
2013-05-29 23:13:22 +00:00
if ( isset ( $pathParts [ 2 ]) && $pathParts [ 2 ] === 'files' && $this -> view -> file_exists ( $path ) && $this -> isEncryptedPath ( $path )) {
2013-04-26 22:05:20 +00:00
2013-05-09 16:16:59 +00:00
// get the size from filesystem
2013-05-27 10:41:55 +00:00
$fullPath = $this -> view -> getLocalFile ( $path );
$size = filesize ( $fullPath );
2013-04-26 22:05:20 +00:00
2013-05-09 16:16:59 +00:00
// calculate last chunk nr
2013-05-27 10:41:55 +00:00
$lastChunkNr = floor ( $size / 8192 );
2013-04-26 22:05:20 +00:00
2013-05-09 16:16:59 +00:00
// open stream
2013-05-29 23:13:22 +00:00
$stream = fopen ( 'crypt://' . $relativePath , " r " );
2013-04-26 22:05:20 +00:00
2013-05-27 10:41:55 +00:00
if ( is_resource ( $stream )) {
2013-05-09 16:16:59 +00:00
// calculate last chunk position
2013-05-27 10:41:55 +00:00
$lastChunckPos = ( $lastChunkNr * 8192 );
2013-04-26 22:05:20 +00:00
2013-05-09 16:16:59 +00:00
// seek to end
2013-05-27 10:41:55 +00:00
fseek ( $stream , $lastChunckPos );
2013-04-26 22:05:20 +00:00
2013-05-09 16:16:59 +00:00
// get the content of the last chunk
2013-05-27 10:41:55 +00:00
$lastChunkContent = fread ( $stream , 8192 );
2013-04-26 22:05:20 +00:00
2013-05-09 16:16:59 +00:00
// calc the real file size with the size of the last chunk
2013-05-27 10:41:55 +00:00
$realSize = (( $lastChunkNr * 6126 ) + strlen ( $lastChunkContent ));
2013-04-26 22:05:20 +00:00
2013-05-09 16:16:59 +00:00
// store file size
$result = $realSize ;
}
}
2013-04-27 21:34:25 +00:00
2013-05-09 16:16:59 +00:00
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-04-27 21:34:25 +00:00
2013-05-09 16:16:59 +00:00
return $result ;
}
2013-05-19 23:24:36 +00:00
2013-05-09 16:16:59 +00:00
/**
* @ brief fix the file size of the encrypted file
2013-05-27 10:41:55 +00:00
* @ param string $path absolute path
* @ return boolean true / false if file is encrypted
2013-05-09 16:16:59 +00:00
*/
2013-05-27 10:41:55 +00:00
public function fixFileSize ( $path ) {
2013-04-27 21:34:25 +00:00
2013-05-09 16:16:59 +00:00
$result = false ;
2013-04-27 21:34:25 +00:00
2013-05-09 16:16:59 +00:00
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy :: $enabled ;
\OC_FileProxy :: $enabled = false ;
2013-04-27 21:34:25 +00:00
2013-05-27 10:41:55 +00:00
$realSize = $this -> getFileSize ( $path );
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
if ( $realSize > 0 ) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$cached = $this -> view -> getFileInfo ( $path );
2013-05-10 22:23:30 +00:00
$cached [ 'encrypted' ] = true ;
2013-04-27 21:34:25 +00:00
2013-05-09 16:16:59 +00:00
// set the size
$cached [ 'unencrypted_size' ] = $realSize ;
2013-04-26 22:05:20 +00:00
2013-05-09 16:16:59 +00:00
// put file info
2013-05-27 10:41:55 +00:00
$this -> view -> putFileInfo ( $path , $cached );
2013-04-26 22:05:20 +00:00
2013-05-09 16:16:59 +00:00
$result = true ;
2013-05-19 23:24:36 +00:00
2013-05-09 16:16:59 +00:00
}
2013-04-26 22:05:20 +00:00
2013-05-09 16:16:59 +00:00
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-04-26 22:05:20 +00:00
2013-05-09 16:16:59 +00:00
return $result ;
}
2013-04-26 22:05:20 +00:00
2013-05-29 23:13:22 +00:00
2013-05-19 23:24:36 +00:00
/**
* @ param $path
* @ return bool
*/
2013-05-27 10:41:55 +00:00
public function isSharedPath ( $path ) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$trimmed = ltrim ( $path , '/' );
$split = explode ( '/' , $trimmed );
2013-05-19 23:24:36 +00:00
2013-05-27 18:44:38 +00:00
if ( isset ( $split [ 2 ]) && $split [ 2 ] === 'Shared' ) {
2013-05-19 23:24:36 +00:00
2013-02-20 19:18:00 +00:00
return true ;
2013-05-19 23:24:36 +00:00
2013-02-20 19:18:00 +00:00
} else {
2013-05-19 23:24:36 +00:00
2013-02-20 19:18:00 +00:00
return false ;
2013-05-19 23:24:36 +00:00
2013-02-20 19:18:00 +00:00
}
2013-05-19 23:24:36 +00:00
2013-02-20 19:18:00 +00:00
}
2013-05-19 23:24:36 +00:00
2013-01-23 19:24:26 +00:00
/**
* @ brief Encrypt all files in a directory
* @ param string $dirPath the directory whose files will be encrypted
2013-05-19 23:24:36 +00:00
* @ param null $legacyPassphrase
* @ param null $newPassphrase
* @ return bool
2013-01-23 19:24:26 +00:00
* @ note Encryption is recursive
*/
2013-05-27 10:41:55 +00:00
public function encryptAll ( $dirPath , $legacyPassphrase = null , $newPassphrase = null ) {
2013-05-17 12:13:05 +00:00
2013-05-27 10:41:55 +00:00
if ( $found = $this -> findEncFiles ( $dirPath )) {
2013-05-17 12:13:05 +00:00
2013-01-29 19:54:40 +00:00
// Disable proxy to prevent file being encrypted twice
\OC_FileProxy :: $enabled = false ;
2013-05-17 12:13:05 +00:00
2013-01-23 19:24:26 +00:00
// Encrypt unencrypted files
2013-05-27 10:41:55 +00:00
foreach ( $found [ 'plain' ] as $plainFile ) {
2013-05-17 12:13:05 +00:00
2013-04-12 13:18:19 +00:00
//relative to data/<user>/file
$relPath = $plainFile [ 'path' ];
2013-05-17 12:13:05 +00:00
2013-04-12 13:18:19 +00:00
//relative to /data
2013-05-24 15:35:00 +00:00
$rawPath = '/' . $this -> userId . '/files/' . $plainFile [ 'path' ];
2013-05-17 12:13:05 +00:00
2013-04-23 16:41:01 +00:00
// Open plain file handle for binary reading
2013-05-24 15:35:00 +00:00
$plainHandle = $this -> view -> fopen ( $rawPath , 'rb' );
2013-05-17 12:13:05 +00:00
2013-05-24 15:35:00 +00:00
// Open enc file handle for binary writing, with same filename as original plain file
$encHandle = fopen ( 'crypt://' . $relPath . '.tmp' , 'wb' );
2013-05-17 12:13:05 +00:00
2013-04-23 16:41:01 +00:00
// Move plain file to a temporary location
2013-05-24 15:35:00 +00:00
$size = stream_copy_to_stream ( $plainHandle , $encHandle );
2013-05-17 12:13:05 +00:00
2013-05-24 15:35:00 +00:00
fclose ( $encHandle );
2013-05-17 12:13:05 +00:00
2013-05-24 15:35:00 +00:00
$fakeRoot = $this -> view -> getRoot ();
$this -> view -> chroot ( '/' . $this -> userId . '/files' );
2013-05-22 00:02:42 +00:00
2013-05-24 15:35:00 +00:00
$this -> view -> rename ( $relPath . '.tmp' , $relPath );
2013-05-17 12:13:05 +00:00
2013-05-24 15:35:00 +00:00
$this -> view -> chroot ( $fakeRoot );
2013-05-17 12:13:05 +00:00
2013-01-31 19:40:51 +00:00
// Add the file to the cache
2013-05-24 15:35:00 +00:00
\OC\Files\Filesystem :: putFileInfo ( $relPath , array ( 'encrypted' => true , 'size' => $size , 'unencrypted_size' => $size ) );
2013-01-23 19:24:26 +00:00
}
2013-05-17 12:13:05 +00:00
2013-01-23 19:24:26 +00:00
// Encrypt legacy encrypted files
2013-05-17 12:13:05 +00:00
if (
2013-05-27 10:41:55 +00:00
! empty ( $legacyPassphrase )
&& ! empty ( $newPassphrase )
2013-01-24 18:37:34 +00:00
) {
2013-05-17 12:13:05 +00:00
2013-05-27 10:41:55 +00:00
foreach ( $found [ 'legacy' ] as $legacyFile ) {
2013-05-17 12:13:05 +00:00
2013-01-24 18:37:34 +00:00
// Fetch data from file
2013-05-27 10:41:55 +00:00
$legacyData = $this -> view -> file_get_contents ( $legacyFile [ 'path' ]);
2013-05-17 12:13:05 +00:00
$sharingEnabled = \OCP\Share :: isEnabled ();
// if file exists try to get sharing users
2013-05-27 10:41:55 +00:00
if ( $this -> view -> file_exists ( $legacyFile [ 'path' ])) {
$uniqueUserIds = $this -> getSharingUsersArray ( $sharingEnabled , $legacyFile [ 'path' ], $this -> userId );
2013-05-17 12:13:05 +00:00
} else {
$uniqueUserIds [] = $this -> userId ;
}
// Fetch public keys for all users who will share the file
2013-05-27 10:41:55 +00:00
$publicKeys = Keymanager :: getPublicKeys ( $this -> view , $uniqueUserIds );
2013-05-17 09:15:36 +00:00
2013-01-24 18:37:34 +00:00
// Recrypt data, generate catfile
2013-05-27 13:31:26 +00:00
$recrypted = Crypt :: legacyKeyRecryptKeyfile ( $legacyData , $legacyPassphrase , $publicKeys );
2013-05-17 12:13:05 +00:00
2013-05-17 09:15:36 +00:00
$rawPath = $legacyFile [ 'path' ];
2013-05-29 23:13:22 +00:00
$relPath = \OCA\Encryption\Helper :: stripUserFilesPath ( $rawPath );
2013-05-17 12:13:05 +00:00
2013-01-31 19:40:51 +00:00
// Save keyfile
2013-05-27 10:41:55 +00:00
Keymanager :: setFileKey ( $this -> view , $relPath , $this -> userId , $recrypted [ 'filekey' ]);
2013-05-17 09:15:36 +00:00
// Save sharekeys to user folders
2013-05-27 10:41:55 +00:00
Keymanager :: setShareKeys ( $this -> view , $relPath , $recrypted [ 'sharekeys' ]);
2013-05-17 09:15:36 +00:00
2013-01-24 18:37:34 +00:00
// Overwrite the existing file with the encrypted one
2013-05-27 10:41:55 +00:00
$this -> view -> file_put_contents ( $rawPath , $recrypted [ 'data' ]);
2013-05-17 12:13:05 +00:00
2013-05-27 10:41:55 +00:00
$size = strlen ( $recrypted [ 'data' ]);
2013-05-17 12:13:05 +00:00
2013-01-31 19:40:51 +00:00
// Add the file to the cache
2013-05-27 10:41:55 +00:00
\OC\Files\Filesystem :: putFileInfo ( $rawPath , array (
'encrypted' => true ,
'size' => $size
), '' );
2013-01-24 18:37:34 +00:00
}
2013-01-23 19:24:26 +00:00
}
2013-05-17 12:13:05 +00:00
2013-01-29 19:54:40 +00:00
\OC_FileProxy :: $enabled = true ;
2013-05-17 12:13:05 +00:00
2013-01-29 19:54:40 +00:00
// If files were found, return true
return true ;
} else {
2013-05-17 12:13:05 +00:00
2013-01-29 19:54:40 +00:00
// If no files were found, return false
return false ;
2012-07-31 18:28:11 +00:00
}
}
2013-05-17 12:13:05 +00:00
2013-02-06 14:30:40 +00:00
/**
* @ brief Return important encryption related paths
* @ param string $pathName Name of the directory to return the path of
* @ return string path
*/
2013-05-27 10:41:55 +00:00
public function getPath ( $pathName ) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
switch ( $pathName ) {
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
case 'publicKeyDir' :
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
return $this -> publicKeyDir ;
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
break ;
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
case 'encryptionDir' :
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
return $this -> encryptionDir ;
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
break ;
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
case 'keyfilesPath' :
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
return $this -> keyfilesPath ;
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
break ;
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
case 'publicKeyPath' :
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
return $this -> publicKeyPath ;
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
break ;
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
case 'privateKeyPath' :
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
return $this -> privateKeyPath ;
2013-05-19 23:24:36 +00:00
2012-11-16 18:31:37 +00:00
break ;
}
2013-05-19 23:24:36 +00:00
2013-05-23 21:56:31 +00:00
return false ;
2012-11-16 18:31:37 +00:00
}
2013-05-19 23:24:36 +00:00
2013-02-12 15:48:04 +00:00
/**
* @ brief get path of a file .
2013-05-19 23:24:36 +00:00
* @ param int $fileId id of the file
* @ return string path of the file
2013-02-12 15:48:04 +00:00
*/
2013-05-27 10:41:55 +00:00
public static function fileIdToPath ( $fileId ) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$sql = 'SELECT `path` FROM `*PREFIX*filecache` WHERE `fileid` = ?' ;
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$query = \OCP\DB :: prepare ( $sql );
$result = $query -> execute ( array ( $fileId ));
2013-05-19 23:24:36 +00:00
2013-05-25 21:07:19 +00:00
$path = false ;
2013-05-27 10:41:55 +00:00
if ( \OCP\DB :: isError ( $result )) {
\OCP\Util :: writeLog ( 'Encryption library' , \OC_DB :: getErrorMessage ( $result ), \OCP\Util :: ERROR );
2013-05-25 21:07:19 +00:00
} else {
2013-05-27 10:41:55 +00:00
if ( $result -> numRows () > 0 ) {
$row = $result -> fetchRow ();
$path = substr ( $row [ 'path' ], strlen ( 'files' ));
}
2013-05-25 19:33:05 +00:00
}
2013-05-25 21:07:19 +00:00
return $path ;
2013-05-19 23:24:36 +00:00
2013-02-12 15:48:04 +00:00
}
2013-05-19 23:24:36 +00:00
2013-02-19 19:16:50 +00:00
/**
* @ brief Filter an array of UIDs to return only ones ready for sharing
* @ param array $unfilteredUsers users to be checked for sharing readiness
2013-05-27 10:41:55 +00:00
* @ return array as multi - dimensional array . keys : ready , unready
2013-02-19 19:16:50 +00:00
*/
2013-05-27 10:41:55 +00:00
public function filterShareReadyUsers ( $unfilteredUsers ) {
2013-05-19 23:24:36 +00:00
2013-02-19 19:16:50 +00:00
// This array will collect the filtered IDs
2013-04-18 18:02:27 +00:00
$readyIds = $unreadyIds = array ();
2013-05-19 23:24:36 +00:00
2013-02-19 19:16:50 +00:00
// Loop through users and create array of UIDs that need new keyfiles
2013-05-27 10:41:55 +00:00
foreach ( $unfilteredUsers as $user ) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$util = new Util ( $this -> view , $user );
2013-05-19 23:24:36 +00:00
2013-02-19 19:16:50 +00:00
// Check that the user is encryption capable, or is the
// public system user 'ownCloud' (for public shares)
2013-05-19 23:24:36 +00:00
if (
2013-05-27 18:44:38 +00:00
$user === $this -> publicShareKeyId
or $user === $this -> recoveryKeyId
2013-05-19 23:24:36 +00:00
or $util -> ready ()
2013-02-19 19:16:50 +00:00
) {
2013-05-19 23:24:36 +00:00
2013-03-29 20:11:29 +00:00
// Construct array of ready UIDs for Keymanager{}
$readyIds [] = $user ;
2013-05-19 23:24:36 +00:00
2013-02-19 19:16:50 +00:00
} else {
2013-05-19 23:24:36 +00:00
2013-03-29 20:11:29 +00:00
// Construct array of unready UIDs for Keymanager{}
$unreadyIds [] = $user ;
2013-05-19 23:24:36 +00:00
2013-02-19 19:16:50 +00:00
// Log warning; we can't do necessary setup here
// because we don't have the user passphrase
2013-05-27 10:41:55 +00:00
\OCP\Util :: writeLog ( 'Encryption library' ,
'"' . $user . '" is not setup for encryption' , \OCP\Util :: WARN );
2013-05-19 23:24:36 +00:00
2013-02-19 19:16:50 +00:00
}
2013-05-19 23:24:36 +00:00
2013-02-19 19:16:50 +00:00
}
2013-05-19 23:24:36 +00:00
return array (
2013-05-23 21:56:31 +00:00
'ready' => $readyIds ,
'unready' => $unreadyIds
2013-03-29 20:11:29 +00:00
);
2013-05-19 23:24:36 +00:00
2013-02-19 19:16:50 +00:00
}
2013-05-19 23:24:36 +00:00
2013-02-20 19:18:00 +00:00
/**
2013-02-27 18:46:44 +00:00
* @ brief Decrypt a keyfile without knowing how it was encrypted
* @ param string $filePath
* @ param string $fileOwner
* @ param string $privateKey
2013-05-29 23:13:22 +00:00
* @ return bool | string
2013-05-19 23:24:36 +00:00
* @ note Checks whether file was encrypted with openssl_seal or
2013-02-27 18:46:44 +00:00
* openssl_encrypt , and decrypts accrdingly
2013-05-19 23:24:36 +00:00
* @ note This was used when 2 types of encryption for keyfiles was used ,
2013-04-10 15:37:03 +00:00
* but now we ' ve switched to exclusively using openssl_seal ()
2013-02-20 19:18:00 +00:00
*/
2013-05-27 10:41:55 +00:00
public function decryptUnknownKeyfile ( $filePath , $fileOwner , $privateKey ) {
2013-02-20 19:18:00 +00:00
// Get the encrypted keyfile
// NOTE: the keyfile format depends on how it was encrypted! At
// this stage we don't know how it was encrypted
2013-05-27 10:41:55 +00:00
$encKeyfile = Keymanager :: getFileKey ( $this -> view , $this -> userId , $filePath );
2013-05-19 23:24:36 +00:00
2013-02-20 19:18:00 +00:00
// We need to decrypt the keyfile
// Has the file been shared yet?
2013-05-19 23:24:36 +00:00
if (
2013-05-27 18:44:38 +00:00
$this -> userId === $fileOwner
2013-05-27 10:41:55 +00:00
&& ! Keymanager :: getShareKey ( $this -> view , $this -> userId , $filePath ) // NOTE: we can't use isShared() here because it's a post share hook so it always returns true
2013-02-20 19:18:00 +00:00
) {
2013-05-19 23:24:36 +00:00
2013-02-20 19:18:00 +00:00
// The file has no shareKey, and its keyfile must be
// decrypted conventionally
2013-05-27 10:41:55 +00:00
$plainKeyfile = Crypt :: keyDecrypt ( $encKeyfile , $privateKey );
2013-05-19 23:24:36 +00:00
2013-02-20 19:18:00 +00:00
} else {
2013-05-19 23:24:36 +00:00
2013-02-20 19:18:00 +00:00
// The file has a shareKey and must use it for decryption
2013-05-27 10:41:55 +00:00
$shareKey = Keymanager :: getShareKey ( $this -> view , $this -> userId , $filePath );
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$plainKeyfile = Crypt :: multiKeyDecrypt ( $encKeyfile , $shareKey , $privateKey );
2013-05-19 23:24:36 +00:00
2013-02-20 19:18:00 +00:00
}
2013-05-19 23:24:36 +00:00
2013-02-27 18:46:44 +00:00
return $plainKeyfile ;
}
2013-05-19 23:24:36 +00:00
2013-02-27 18:46:44 +00:00
/**
* @ brief Encrypt keyfile to multiple users
2013-05-19 23:24:36 +00:00
* @ param Session $session
2013-02-27 18:46:44 +00:00
* @ param array $users list of users which should be able to access the file
* @ param string $filePath path of the file to be shared
2013-05-19 23:24:36 +00:00
* @ return bool
2013-02-27 18:46:44 +00:00
*/
2013-05-27 10:41:55 +00:00
public function setSharedFileKeyfiles ( Session $session , array $users , $filePath ) {
2013-05-19 23:24:36 +00:00
2013-02-27 18:46:44 +00:00
// Make sure users are capable of sharing
2013-05-27 10:41:55 +00:00
$filteredUids = $this -> filterShareReadyUsers ( $users );
2013-05-19 23:24:36 +00:00
2013-05-09 12:43:06 +00:00
// If we're attempting to share to unready users
2013-05-27 10:41:55 +00:00
if ( ! empty ( $filteredUids [ 'unready' ])) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
\OCP\Util :: writeLog ( 'Encryption library' ,
'Sharing to these user(s) failed as they are unready for encryption:"'
. print_r ( $filteredUids [ 'unready' ], 1 ), \OCP\Util :: WARN );
2013-05-19 23:24:36 +00:00
2013-05-09 12:43:06 +00:00
return false ;
2013-05-19 23:24:36 +00:00
2013-03-29 20:11:29 +00:00
}
2013-05-19 23:24:36 +00:00
2013-02-27 18:46:44 +00:00
// Get public keys for each user, ready for generating sharekeys
2013-05-27 10:41:55 +00:00
$userPubKeys = Keymanager :: getPublicKeys ( $this -> view , $filteredUids [ 'ready' ]);
2013-05-19 23:24:36 +00:00
2013-05-09 12:43:06 +00:00
// Note proxy status then disable it
$proxyStatus = \OC_FileProxy :: $enabled ;
2013-02-27 18:46:44 +00:00
\OC_FileProxy :: $enabled = false ;
// Get the current users's private key for decrypting existing keyfile
$privateKey = $session -> getPrivateKey ();
2013-05-16 12:53:04 +00:00
2013-05-27 10:41:55 +00:00
$fileOwner = \OC\Files\Filesystem :: getOwner ( $filePath );
2013-05-19 23:24:36 +00:00
2013-03-19 18:53:15 +00:00
// Decrypt keyfile
2013-05-27 10:41:55 +00:00
$plainKeyfile = $this -> decryptUnknownKeyfile ( $filePath , $fileOwner , $privateKey );
2013-05-19 23:24:36 +00:00
2013-02-20 19:18:00 +00:00
// Re-enc keyfile to (additional) sharekeys
2013-05-27 10:41:55 +00:00
$multiEncKey = Crypt :: multiKeyEncrypt ( $plainKeyfile , $userPubKeys );
2013-05-19 23:24:36 +00:00
2013-02-26 18:11:29 +00:00
// Save the recrypted key to it's owner's keyfiles directory
// Save new sharekeys to all necessary user directory
2013-05-19 23:24:36 +00:00
if (
2013-05-27 10:41:55 +00:00
! Keymanager :: setFileKey ( $this -> view , $filePath , $fileOwner , $multiEncKey [ 'data' ])
|| ! Keymanager :: setShareKeys ( $this -> view , $filePath , $multiEncKey [ 'keys' ])
2013-02-26 18:11:29 +00:00
) {
2013-02-20 19:18:00 +00:00
2013-05-27 10:41:55 +00:00
\OCP\Util :: writeLog ( 'Encryption library' ,
'Keyfiles could not be saved for users sharing ' . $filePath , \OCP\Util :: ERROR );
2013-05-19 23:24:36 +00:00
2013-05-09 12:43:06 +00:00
return false ;
2013-02-20 19:18:00 +00:00
}
2013-05-19 23:24:36 +00:00
2013-05-09 12:43:06 +00:00
// Return proxy to original status
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-02-20 19:18:00 +00:00
return true ;
}
2013-05-19 23:24:36 +00:00
2013-04-16 16:29:22 +00:00
/**
* @ brief Find , sanitise and format users sharing a file
* @ note This wraps other methods into a portable bundle
*/
2013-05-27 10:41:55 +00:00
public function getSharingUsersArray ( $sharingEnabled , $filePath , $currentUserId = false ) {
2012-07-11 16:51:27 +00:00
2013-04-16 16:29:22 +00:00
// Check if key recovery is enabled
2013-05-04 14:14:38 +00:00
if (
2013-05-27 10:41:55 +00:00
\OC_Appconfig :: getValue ( 'files_encryption' , 'recoveryAdminEnabled' )
2013-05-04 14:14:38 +00:00
&& $this -> recoveryEnabledForUser ()
) {
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
$recoveryEnabled = true ;
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
} else {
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
$recoveryEnabled = false ;
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
}
2013-05-19 23:24:36 +00:00
2013-04-16 16:29:22 +00:00
// Make sure that a share key is generated for the owner too
2013-05-27 10:41:55 +00:00
list ( $owner , $ownerPath ) = $this -> getUidAndFilename ( $filePath );
2013-05-19 23:24:36 +00:00
2013-05-23 21:56:31 +00:00
$userIds = array ();
2013-05-27 10:41:55 +00:00
if ( $sharingEnabled ) {
2013-04-18 15:53:59 +00:00
2013-04-16 16:29:22 +00:00
// Find out who, if anyone, is sharing the file
2013-05-29 11:10:26 +00:00
$result = \OCP\Share :: getUsersSharingFile ( $ownerPath , $owner , true );
2013-05-13 15:40:57 +00:00
$userIds = $result [ 'users' ];
2013-05-27 10:41:55 +00:00
if ( $result [ 'public' ]) {
2013-05-13 15:40:57 +00:00
$userIds [] = $this -> publicShareKeyId ;
}
2013-05-19 23:24:36 +00:00
2013-04-16 16:29:22 +00:00
}
2013-05-19 23:24:36 +00:00
2013-04-16 16:29:22 +00:00
// If recovery is enabled, add the
// Admin UID to list of users to share to
2013-05-27 10:41:55 +00:00
if ( $recoveryEnabled ) {
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
// Find recoveryAdmin user ID
2013-05-27 10:41:55 +00:00
$recoveryKeyId = \OC_Appconfig :: getValue ( 'files_encryption' , 'recoveryKeyId' );
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
// Add recoveryAdmin to list of users sharing
2013-05-13 15:26:21 +00:00
$userIds [] = $recoveryKeyId ;
2013-05-19 23:24:36 +00:00
2013-04-16 16:29:22 +00:00
}
2013-04-22 10:25:55 +00:00
2013-05-04 14:14:38 +00:00
// add current user if given
2013-05-27 18:44:38 +00:00
if ( $currentUserId !== false ) {
2013-05-19 23:24:36 +00:00
2013-05-09 12:43:06 +00:00
$userIds [] = $currentUserId ;
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
}
2013-04-22 10:25:55 +00:00
2013-04-16 16:29:22 +00:00
// Remove duplicate UIDs
2013-05-27 10:41:55 +00:00
$uniqueUserIds = array_unique ( $userIds );
2013-05-19 23:24:36 +00:00
2013-04-16 16:29:22 +00:00
return $uniqueUserIds ;
}
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
/**
* @ brief Set file migration status for user
2013-05-19 23:24:36 +00:00
* @ param $status
2013-05-09 12:43:06 +00:00
* @ return bool
2013-05-04 14:14:38 +00:00
*/
2013-06-11 10:03:50 +00:00
private function setMigrationStatus ( $status ) {
2013-05-19 23:24:36 +00:00
2013-06-10 07:53:29 +00:00
$sql = 'UPDATE `*PREFIX*encryption` SET `migration_status` = ? WHERE `uid` = ?' ;
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$args = array (
$status ,
$this -> userId
);
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$query = \OCP\DB :: prepare ( $sql );
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
if ( $query -> execute ( $args )) {
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
return true ;
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
} else {
2013-06-11 10:03:50 +00:00
\OCP\Util :: writeLog ( 'Encryption library' , " Could not set migration status for " . $this -> userId , \OCP\Util :: ERROR );
2013-05-04 14:14:38 +00:00
return false ;
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
}
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
}
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
/**
2013-06-11 10:03:50 +00:00
* @ brief start migration mode to initially encrypt users data
* @ return boolean
*/
public function beginMigration () {
$return = false ;
$transaction = \OC_DB :: beginTransaction ();
if ( $transaction === false ) {
\OCP\Util :: writeLog ( 'Encryption library' , " Your database migration doesn't support transactions " , \OCP\Util :: WARN );
}
$migrationStatus = $this -> getMigrationStatus ();
if ( $migrationStatus === '0' ) {
$return = $this -> setMigrationStatus ( - 1 );
if ( $return === true ) {
\OCP\Util :: writeLog ( 'Encryption library' , " Enter migration mode for initial encryption for user " . $this -> userId , \OCP\Util :: INFO );
} else {
\OCP\Util :: writeLog ( 'Encryption library' , " Could not activate migration mode for " . $this -> userId . " , encryption aborted " , \OCP\Util :: ERROR );
}
} else {
\OCP\Util :: writeLog ( 'Encryption library' , " Another process already performs the migration for user " . $this -> userId , \OCP\Util :: INFO );
}
\OC_DB :: commit ();
return $return ;
}
/**
* @ brief close migration mode after users data has been encrypted successfully
* @ return boolean
*/
public function finishMigration () {
$return = false ;
$transaction = \OC_DB :: beginTransaction ();
if ( $transaction === false ) {
\OCP\Util :: writeLog ( 'Encryption library' , " Your database migration doesn't support transactions " , \OCP\Util :: WARN );
}
$migrationStatus = $this -> getMigrationStatus ();
if ( $migrationStatus === '-1' ) {
$return = $this -> setMigrationStatus ( 1 );
if ( $return === true ) {
\OCP\Util :: writeLog ( 'Encryption library' , " Leave migration mode for: " . $this -> userId . " successfully. " , \OCP\Util :: INFO );
} else {
\OCP\Util :: writeLog ( 'Encryption library' , " Could not deactivate migration mode for " . $this -> userId , \OCP\Util :: ERROR );
}
} else {
\OCP\Util :: writeLog ( 'Encryption library' , " Someone else finished the migration mode to early for user " . $this -> userId , \OCP\Util :: ERROR );
}
\OC_DB :: commit ();
return $return ;
}
/**
* @ brief check if files are already migrated to the encryption system
* @ return '1' = yes , '0' = no , '-1' = migration in progress , false = no record
2013-05-19 23:24:36 +00:00
* @ note If records are not being returned , check for a hidden space
2013-05-04 14:14:38 +00:00
* at the start of the uid in db
*/
2013-06-11 10:03:50 +00:00
private function getMigrationStatus () {
2013-05-19 23:24:36 +00:00
2013-06-10 07:53:29 +00:00
$sql = 'SELECT `migration_status` FROM `*PREFIX*encryption` WHERE `uid` = ?' ;
2013-05-04 14:14:38 +00:00
2013-05-27 10:41:55 +00:00
$args = array ( $this -> userId );
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$query = \OCP\DB :: prepare ( $sql );
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$result = $query -> execute ( $args );
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
$migrationStatus = array ();
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
if ( \OCP\DB :: isError ( $result )) {
\OCP\Util :: writeLog ( 'Encryption library' , \OC_DB :: getErrorMessage ( $result ), \OCP\Util :: ERROR );
2013-05-25 21:07:19 +00:00
} else {
2013-05-27 10:41:55 +00:00
if ( $result -> numRows () > 0 ) {
$row = $result -> fetchRow ();
if ( isset ( $row [ 'migration_status' ])) {
$migrationStatus [] = $row [ 'migration_status' ];
}
2013-05-25 21:07:19 +00:00
}
2013-05-04 14:14:38 +00:00
}
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
// If no record is found
2013-05-27 10:41:55 +00:00
if ( empty ( $migrationStatus )) {
2013-06-11 10:03:50 +00:00
\OCP\Util :: writeLog ( 'Encryption library' , " Could not get migration status for " . $this -> userId . " , no record found " , \OCP\Util :: ERROR );
2013-05-04 14:14:38 +00:00
return false ;
2013-05-19 23:24:36 +00:00
// If a record is found
2013-05-04 14:14:38 +00:00
} else {
return $migrationStatus [ 0 ];
}
2013-05-19 23:24:36 +00:00
2013-05-04 14:14:38 +00:00
}
2013-05-19 23:24:36 +00:00
2013-03-29 20:11:29 +00:00
/**
2013-03-26 11:39:55 +00:00
* @ brief get uid of the owners of the file and the path to the file
2013-05-19 23:24:36 +00:00
* @ param string $path Path of the file to check
2013-05-29 23:13:22 +00:00
* @ throws \Exception
2013-05-19 23:24:36 +00:00
* @ note $shareFilePath must be relative to data / UID / files . Files
2013-04-10 15:37:03 +00:00
* relative to / Shared are also acceptable
2013-03-26 11:39:55 +00:00
* @ return array
*/
2013-05-27 10:41:55 +00:00
public function getUidAndFilename ( $path ) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$view = new \OC\Files\View ( $this -> userFilesDir );
$fileOwnerUid = $view -> getOwner ( $path );
2013-04-18 14:37:49 +00:00
2013-05-19 23:24:36 +00:00
// handle public access
2013-05-27 10:41:55 +00:00
if ( $this -> isPublic ) {
2013-05-19 23:24:36 +00:00
$filename = $path ;
$fileOwnerUid = $GLOBALS [ 'fileOwner' ];
2013-04-10 15:37:03 +00:00
2013-05-27 10:41:55 +00:00
return array (
$fileOwnerUid ,
$filename
);
2013-05-19 23:24:36 +00:00
} else {
2013-05-13 19:24:59 +00:00
2013-05-19 23:24:36 +00:00
// Check that UID is valid
2013-05-27 10:41:55 +00:00
if ( ! \OCP\User :: userExists ( $fileOwnerUid )) {
throw new \Exception (
'Could not find owner (UID = "' . var_export ( $fileOwnerUid , 1 ) . '") of file "' . $path . '"' );
2013-05-19 23:24:36 +00:00
}
2013-05-13 19:24:59 +00:00
2013-05-19 23:24:36 +00:00
// NOTE: Bah, this dependency should be elsewhere
2013-05-27 10:41:55 +00:00
\OC\Files\Filesystem :: initMountPoints ( $fileOwnerUid );
2013-05-13 19:24:59 +00:00
2013-05-19 23:24:36 +00:00
// If the file owner is the currently logged in user
2013-05-27 18:44:38 +00:00
if ( $fileOwnerUid === $this -> userId ) {
2013-05-13 19:24:59 +00:00
2013-05-19 23:24:36 +00:00
// Assume the path supplied is correct
$filename = $path ;
2013-05-13 19:24:59 +00:00
2013-05-19 23:24:36 +00:00
} else {
2013-05-13 19:24:59 +00:00
2013-05-27 10:41:55 +00:00
$info = $view -> getFileInfo ( $path );
$ownerView = new \OC\Files\View ( '/' . $fileOwnerUid . '/files' );
2013-05-13 19:24:59 +00:00
2013-05-19 23:24:36 +00:00
// Fetch real file path from DB
2013-05-27 10:41:55 +00:00
$filename = $ownerView -> getPath ( $info [ 'fileid' ]); // TODO: Check that this returns a path without including the user data dir
2013-05-13 19:24:59 +00:00
2013-05-19 23:24:36 +00:00
}
2013-05-13 19:24:59 +00:00
2013-05-27 10:41:55 +00:00
return array (
$fileOwnerUid ,
$filename
);
2013-05-19 23:24:36 +00:00
}
2013-05-13 19:24:59 +00:00
2013-03-26 11:39:55 +00:00
}
2013-04-22 09:58:39 +00:00
/**
2013-05-23 21:56:31 +00:00
* @ brief go recursively through a dir and collect all files and sub files .
2013-05-19 23:24:36 +00:00
* @ param string $dir relative to the users files folder
2013-04-22 09:58:39 +00:00
* @ return array with list of files relative to the users files folder
*/
2013-05-27 10:41:55 +00:00
public function getAllFiles ( $dir ) {
2013-05-19 23:24:36 +00:00
2013-04-22 09:58:39 +00:00
$result = array ();
2013-05-01 09:04:40 +00:00
2013-05-29 17:11:39 +00:00
$content = $this -> view -> getDirectoryContent ( \OC\Files\Filesystem :: normalizePath ( $this -> userFilesDir . '/' . $dir ));
2013-05-01 09:04:40 +00:00
2013-05-09 12:43:06 +00:00
// handling for re shared folders
2013-05-29 23:13:22 +00:00
$pathSplit = explode ( '/' , $dir );
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
foreach ( $content as $c ) {
2013-05-19 23:24:36 +00:00
2013-05-29 23:13:22 +00:00
$sharedPart = $pathSplit [ sizeof ( $pathSplit ) - 1 ];
2013-05-27 10:41:55 +00:00
$targetPathSplit = array_reverse ( explode ( '/' , $c [ 'path' ]));
2013-05-05 21:41:42 +00:00
2013-05-09 12:43:06 +00:00
$path = '' ;
2013-05-05 21:41:42 +00:00
2013-05-09 12:43:06 +00:00
// rebuild path
2013-05-27 10:41:55 +00:00
foreach ( $targetPathSplit as $pathPart ) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
if ( $pathPart !== $sharedPart ) {
2013-05-19 23:24:36 +00:00
2013-05-09 12:43:06 +00:00
$path = '/' . $pathPart . $path ;
2013-05-19 23:24:36 +00:00
2013-05-09 12:43:06 +00:00
} else {
2013-05-19 23:24:36 +00:00
2013-05-09 12:43:06 +00:00
break ;
2013-05-19 23:24:36 +00:00
2013-05-09 12:43:06 +00:00
}
2013-05-19 23:24:36 +00:00
2013-05-09 12:43:06 +00:00
}
2013-05-05 21:41:42 +00:00
2013-05-19 23:24:36 +00:00
$path = $dir . $path ;
2013-05-27 18:44:38 +00:00
if ( $c [ 'type' ] === 'dir' ) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$result = array_merge ( $result , $this -> getAllFiles ( $path ));
2013-05-05 21:41:42 +00:00
2013-04-22 09:58:39 +00:00
} else {
2013-05-19 23:24:36 +00:00
2013-05-09 12:43:06 +00:00
$result [] = $path ;
2013-05-19 23:24:36 +00:00
2013-04-22 09:58:39 +00:00
}
}
2013-05-19 23:24:36 +00:00
2013-04-22 09:58:39 +00:00
return $result ;
2013-05-19 23:24:36 +00:00
2013-04-22 09:58:39 +00:00
}
2013-05-09 16:16:59 +00:00
/**
* @ brief get shares parent .
* @ param int $id of the current share
* @ return array of the parent
*/
2013-05-27 10:41:55 +00:00
public static function getShareParent ( $id ) {
2013-05-04 01:37:22 +00:00
2013-05-27 10:41:55 +00:00
$sql = 'SELECT `file_target`, `item_type` FROM `*PREFIX*share` WHERE `id` = ?' ;
2013-05-04 01:37:22 +00:00
2013-05-27 10:41:55 +00:00
$query = \OCP\DB :: prepare ( $sql );
$result = $query -> execute ( array ( $id ));
2013-05-04 01:37:22 +00:00
2013-05-25 21:07:19 +00:00
$row = array ();
2013-05-27 10:41:55 +00:00
if ( \OCP\DB :: isError ( $result )) {
\OCP\Util :: writeLog ( 'Encryption library' , \OC_DB :: getErrorMessage ( $result ), \OCP\Util :: ERROR );
2013-05-25 21:07:19 +00:00
} else {
2013-05-27 10:41:55 +00:00
if ( $result -> numRows () > 0 ) {
$row = $result -> fetchRow ();
}
2013-05-25 19:33:05 +00:00
}
2013-05-09 16:16:59 +00:00
return $row ;
2013-05-04 01:37:22 +00:00
2013-05-09 16:16:59 +00:00
}
2013-05-05 21:41:42 +00:00
2013-05-15 22:34:45 +00:00
/**
* @ brief get shares parent .
* @ param int $id of the current share
* @ return array of the parent
*/
2013-05-27 10:41:55 +00:00
public static function getParentFromShare ( $id ) {
2013-05-15 22:34:45 +00:00
2013-05-27 10:41:55 +00:00
$sql = 'SELECT `parent` FROM `*PREFIX*share` WHERE `id` = ?' ;
2013-05-15 22:34:45 +00:00
2013-05-27 10:41:55 +00:00
$query = \OCP\DB :: prepare ( $sql );
$result = $query -> execute ( array ( $id ));
2013-05-15 22:34:45 +00:00
2013-05-25 21:07:19 +00:00
$row = array ();
2013-05-27 10:41:55 +00:00
if ( \OCP\DB :: isError ( $result )) {
\OCP\Util :: writeLog ( 'Encryption library' , \OC_DB :: getErrorMessage ( $result ), \OCP\Util :: ERROR );
2013-05-25 21:07:19 +00:00
} else {
2013-05-27 10:41:55 +00:00
if ( $result -> numRows () > 0 ) {
$row = $result -> fetchRow ();
}
2013-05-25 19:33:05 +00:00
}
2013-05-15 22:34:45 +00:00
return $row ;
}
2013-05-09 16:16:59 +00:00
/**
* @ brief get owner of the shared files .
2013-05-19 23:24:36 +00:00
* @ param $id
* @ internal param int $Id of a share
* @ return string owner
2013-05-09 16:16:59 +00:00
*/
2013-05-27 10:41:55 +00:00
public function getOwnerFromSharedFile ( $id ) {
2013-05-23 21:56:31 +00:00
2013-05-27 10:41:55 +00:00
$query = \OCP\DB :: prepare ( 'SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?' , 1 );
2013-05-25 19:33:05 +00:00
2013-05-27 10:41:55 +00:00
$result = $query -> execute ( array ( $id ));
2013-05-25 19:33:05 +00:00
2013-05-25 21:07:19 +00:00
$source = array ();
2013-05-27 10:41:55 +00:00
if ( \OCP\DB :: isError ( $result )) {
\OCP\Util :: writeLog ( 'Encryption library' , \OC_DB :: getErrorMessage ( $result ), \OCP\Util :: ERROR );
2013-05-25 21:07:19 +00:00
} else {
2013-05-27 10:41:55 +00:00
if ( $result -> numRows () > 0 ) {
$source = $result -> fetchRow ();
}
2013-05-25 19:33:05 +00:00
}
2013-05-23 21:56:31 +00:00
$fileOwner = false ;
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
if ( isset ( $source [ 'parent' ])) {
2013-05-09 16:16:59 +00:00
$parent = $source [ 'parent' ];
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
while ( isset ( $parent )) {
2013-05-19 23:24:36 +00:00
2013-05-27 10:41:55 +00:00
$query = \OCP\DB :: prepare ( 'SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?' , 1 );
2013-05-25 19:33:05 +00:00
2013-05-27 10:41:55 +00:00
$result = $query -> execute ( array ( $parent ));
2013-05-25 19:33:05 +00:00
2013-05-25 21:07:19 +00:00
$item = array ();
2013-05-27 10:41:55 +00:00
if ( \OCP\DB :: isError ( $result )) {
\OCP\Util :: writeLog ( 'Encryption library' , \OC_DB :: getErrorMessage ( $result ), \OCP\Util :: ERROR );
2013-05-25 21:07:19 +00:00
} else {
2013-05-27 10:41:55 +00:00
if ( $result -> numRows () > 0 ) {
$item = $result -> fetchRow ();
}
2013-05-25 19:33:05 +00:00
}
2013-05-27 10:41:55 +00:00
if ( isset ( $item [ 'parent' ])) {
2013-05-19 23:24:36 +00:00
2013-05-09 16:16:59 +00:00
$parent = $item [ 'parent' ];
2013-05-19 23:24:36 +00:00
2013-05-09 16:16:59 +00:00
} else {
2013-05-19 23:24:36 +00:00
2013-05-09 16:16:59 +00:00
$fileOwner = $item [ 'uid_owner' ];
2013-05-19 23:24:36 +00:00
2013-05-09 16:16:59 +00:00
break ;
2013-05-19 23:24:36 +00:00
2013-05-09 16:16:59 +00:00
}
}
2013-05-19 23:24:36 +00:00
2013-05-09 16:16:59 +00:00
} else {
2013-05-19 23:24:36 +00:00
2013-05-09 16:16:59 +00:00
$fileOwner = $source [ 'uid_owner' ];
2013-05-19 23:24:36 +00:00
2013-05-09 12:43:06 +00:00
}
return $fileOwner ;
2013-05-19 23:24:36 +00:00
2013-05-09 12:43:06 +00:00
}
2013-05-05 21:41:42 +00:00
2013-05-19 23:24:36 +00:00
/**
* @ return string
*/
2013-05-23 21:56:31 +00:00
public function getUserId () {
2013-05-19 23:24:36 +00:00
return $this -> userId ;
}
2013-05-13 19:24:59 +00:00
2013-05-19 23:24:36 +00:00
/**
* @ return string
*/
2013-05-23 21:56:31 +00:00
public function getUserFilesDir () {
2013-05-19 23:24:36 +00:00
return $this -> userFilesDir ;
}
2013-05-13 19:24:59 +00:00
2013-05-19 23:24:36 +00:00
/**
* @ param $password
* @ return bool
*/
2013-05-27 10:41:55 +00:00
public function checkRecoveryPassword ( $password ) {
2013-05-15 14:12:20 +00:00
$pathKey = '/owncloud_private_key/' . $this -> recoveryKeyId . " .private.key " ;
$pathControlData = '/control-file/controlfile.enc' ;
$proxyStatus = \OC_FileProxy :: $enabled ;
\OC_FileProxy :: $enabled = false ;
2013-05-27 10:41:55 +00:00
$recoveryKey = $this -> view -> file_get_contents ( $pathKey );
2013-05-15 14:12:20 +00:00
2013-05-27 10:41:55 +00:00
$decryptedRecoveryKey = Crypt :: symmetricDecryptFileContent ( $recoveryKey , $password );
2013-05-15 14:12:20 +00:00
2013-05-27 10:41:55 +00:00
$controlData = $this -> view -> file_get_contents ( $pathControlData );
$decryptedControlData = Crypt :: keyDecrypt ( $controlData , $decryptedRecoveryKey );
2013-05-15 14:12:20 +00:00
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-05-27 10:41:55 +00:00
if ( $decryptedControlData === 'ownCloud' ) {
2013-05-15 14:12:20 +00:00
return true ;
2013-05-19 23:24:36 +00:00
}
2013-05-15 14:12:20 +00:00
return false ;
}
2013-05-19 23:24:36 +00:00
/**
* @ return string
*/
2013-05-23 21:56:31 +00:00
public function getRecoveryKeyId () {
2013-05-15 14:12:20 +00:00
return $this -> recoveryKeyId ;
}
2013-05-15 15:56:45 +00:00
/**
* @ brief add recovery key to all encrypted files
*/
2013-05-27 10:41:55 +00:00
public function addRecoveryKeys ( $path = '/' ) {
$dirContent = $this -> view -> getDirectoryContent ( $this -> keyfilesPath . $path );
foreach ( $dirContent as $item ) {
2013-05-23 21:56:31 +00:00
// get relative path from files_encryption/keyfiles/
2013-05-27 10:41:55 +00:00
$filePath = substr ( $item [ 'path' ], strlen ( 'files_encryption/keyfiles' ));
2013-05-27 18:44:38 +00:00
if ( $item [ 'type' ] === 'dir' ) {
2013-05-27 10:41:55 +00:00
$this -> addRecoveryKeys ( $filePath . '/' );
2013-05-15 15:56:45 +00:00
} else {
2013-05-28 15:04:35 +00:00
$session = new \OCA\Encryption\Session ( new \OC_FilesystemView ( '/' ));
2013-05-15 15:56:45 +00:00
$sharingEnabled = \OCP\Share :: isEnabled ();
2013-05-29 10:23:33 +00:00
// remove '.key' extension from path e.g. 'file.txt.key' to 'file.txt'
2013-05-27 10:41:55 +00:00
$file = substr ( $filePath , 0 , - 4 );
$usersSharing = $this -> getSharingUsersArray ( $sharingEnabled , $file );
$this -> setSharedFileKeyfiles ( $session , $usersSharing , $file );
2013-05-15 15:56:45 +00:00
}
}
}
2013-05-16 12:53:04 +00:00
/**
2013-05-15 15:56:45 +00:00
* @ brief remove recovery key to all encrypted files
*/
2013-05-27 10:41:55 +00:00
public function removeRecoveryKeys ( $path = '/' ) {
$dirContent = $this -> view -> getDirectoryContent ( $this -> keyfilesPath . $path );
foreach ( $dirContent as $item ) {
2013-05-23 21:56:31 +00:00
// get relative path from files_encryption/keyfiles
2013-05-27 10:41:55 +00:00
$filePath = substr ( $item [ 'path' ], strlen ( 'files_encryption/keyfiles' ));
2013-05-27 18:44:38 +00:00
if ( $item [ 'type' ] === 'dir' ) {
2013-05-27 10:41:55 +00:00
$this -> removeRecoveryKeys ( $filePath . '/' );
2013-05-15 15:56:45 +00:00
} else {
2013-05-29 10:23:33 +00:00
// remove '.key' extension from path e.g. 'file.txt.key' to 'file.txt'
2013-05-27 10:41:55 +00:00
$file = substr ( $filePath , 0 , - 4 );
$this -> view -> unlink ( $this -> shareKeysPath . '/' . $file . '.' . $this -> recoveryKeyId . '.shareKey' );
2013-05-15 15:56:45 +00:00
}
}
}
2013-05-16 12:53:04 +00:00
/**
* @ brief decrypt given file with recovery key and encrypt it again to the owner and his new key
2013-05-19 23:24:36 +00:00
* @ param string $file
* @ param string $privateKey recovery key to decrypt the file
2013-05-16 12:53:04 +00:00
*/
2013-05-27 10:41:55 +00:00
private function recoverFile ( $file , $privateKey ) {
2013-05-16 12:53:04 +00:00
$sharingEnabled = \OCP\Share :: isEnabled ();
// Find out who, if anyone, is sharing the file
2013-05-27 10:41:55 +00:00
if ( $sharingEnabled ) {
2013-05-29 11:10:26 +00:00
$result = \OCP\Share :: getUsersSharingFile ( $file , $this -> userId , true );
2013-05-16 12:53:04 +00:00
$userIds = $result [ 'users' ];
$userIds [] = $this -> recoveryKeyId ;
2013-05-27 10:41:55 +00:00
if ( $result [ 'public' ]) {
2013-05-16 12:53:04 +00:00
$userIds [] = $this -> publicShareKeyId ;
}
} else {
2013-05-27 10:41:55 +00:00
$userIds = array (
$this -> userId ,
$this -> recoveryKeyId
);
2013-05-16 12:53:04 +00:00
}
2013-05-27 10:41:55 +00:00
$filteredUids = $this -> filterShareReadyUsers ( $userIds );
2013-05-16 12:53:04 +00:00
$proxyStatus = \OC_FileProxy :: $enabled ;
\OC_FileProxy :: $enabled = false ;
//decrypt file key
2013-05-27 10:41:55 +00:00
$encKeyfile = $this -> view -> file_get_contents ( $this -> keyfilesPath . $file . " .key " );
$shareKey = $this -> view -> file_get_contents (
$this -> shareKeysPath . $file . " . " . $this -> recoveryKeyId . " .shareKey " );
$plainKeyfile = Crypt :: multiKeyDecrypt ( $encKeyfile , $shareKey , $privateKey );
2013-05-16 12:53:04 +00:00
// encrypt file key again to all users, this time with the new public key for the recovered use
2013-05-27 10:41:55 +00:00
$userPubKeys = Keymanager :: getPublicKeys ( $this -> view , $filteredUids [ 'ready' ]);
$multiEncKey = Crypt :: multiKeyEncrypt ( $plainKeyfile , $userPubKeys );
2013-05-16 12:53:04 +00:00
// write new keys to filesystem TDOO!
2013-05-27 10:41:55 +00:00
$this -> view -> file_put_contents ( $this -> keyfilesPath . $file . '.key' , $multiEncKey [ 'data' ]);
foreach ( $multiEncKey [ 'keys' ] as $userId => $shareKey ) {
2013-05-19 23:24:36 +00:00
$shareKeyPath = $this -> shareKeysPath . $file . '.' . $userId . '.shareKey' ;
2013-05-27 10:41:55 +00:00
$this -> view -> file_put_contents ( $shareKeyPath , $shareKey );
2013-05-16 12:53:04 +00:00
}
// Return proxy to original status
\OC_FileProxy :: $enabled = $proxyStatus ;
}
/**
* @ brief collect all files and recover them one by one
2013-05-19 23:24:36 +00:00
* @ param string $path to look for files keys
* @ param string $privateKey private recovery key which is used to decrypt the files
2013-05-16 12:53:04 +00:00
*/
2013-05-27 10:41:55 +00:00
private function recoverAllFiles ( $path , $privateKey ) {
$dirContent = $this -> view -> getDirectoryContent ( $this -> keyfilesPath . $path );
foreach ( $dirContent as $item ) {
2013-05-29 10:23:33 +00:00
// get relative path from files_encryption/keyfiles
$filePath = substr ( $item [ 'path' ], strlen ( 'files_encryption/keyfiles' ));
2013-05-27 18:44:38 +00:00
if ( $item [ 'type' ] === 'dir' ) {
2013-05-27 10:41:55 +00:00
$this -> recoverAllFiles ( $filePath . '/' , $privateKey );
2013-05-16 12:53:04 +00:00
} else {
2013-05-29 10:23:33 +00:00
// remove '.key' extension from path e.g. 'file.txt.key' to 'file.txt'
2013-05-27 10:41:55 +00:00
$file = substr ( $filePath , 0 , - 4 );
$this -> recoverFile ( $file , $privateKey );
2013-05-16 12:53:04 +00:00
}
}
}
/**
* @ brief recover users files in case of password lost
2013-05-19 23:24:36 +00:00
* @ param string $recoveryPassword
2013-05-16 12:53:04 +00:00
*/
2013-05-27 10:41:55 +00:00
public function recoverUsersFiles ( $recoveryPassword ) {
2013-05-16 12:53:04 +00:00
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy :: $enabled ;
\OC_FileProxy :: $enabled = false ;
2013-05-27 10:41:55 +00:00
$encryptedKey = $this -> view -> file_get_contents (
'/owncloud_private_key/' . $this -> recoveryKeyId . '.private.key' );
$privateKey = Crypt :: symmetricDecryptFileContent ( $encryptedKey , $recoveryPassword );
2013-05-16 12:53:04 +00:00
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-05-27 10:41:55 +00:00
$this -> recoverAllFiles ( '/' , $privateKey );
2013-05-16 12:53:04 +00:00
}
2013-05-23 18:30:07 +00:00
2013-05-29 17:11:39 +00:00
/**
* Get the path including the storage mount point
* @ param int $id
* @ return string the path including the mount point like AmazonS3 / folder / file . txt
*/
public function getPathWithMountPoint ( $id ) {
list ( $storage , $internalPath ) = \OC\Files\Cache\Cache :: getById ( $id );
$mount = \OC\Files\Filesystem :: getMountByStorageId ( $storage );
$mountPoint = $mount [ 0 ] -> getMountPoint ();
$path = \OC\Files\Filesystem :: normalizePath ( $mountPoint . '/' . $internalPath );
// reformat the path to be relative e.g. /user/files/folder becomes /folder/
2013-05-29 23:13:22 +00:00
$relativePath = \OCA\Encryption\Helper :: stripUserFilesPath ( $path );
2013-05-29 17:11:39 +00:00
return $relativePath ;
}
2012-07-11 16:51:27 +00:00
}