2012-07-11 20:51:27 +04:00
< ? php
/**
2015-03-26 13:44:34 +03:00
* @ author Arthur Schiwon < blizzz @ owncloud . com >
* @ author Björn Schießle < schiessle @ owncloud . com >
* @ author Florin Peter < github @ florin - peter . de >
* @ author jknockaert < jasper @ knockaert . nl >
* @ author Joas Schilling < nickvergessen @ owncloud . com >
* @ author Jörn Friedrich Dreyer < jfd @ butonic . de >
* @ author Markus Goetz < markus @ woboq . com >
* @ author Morris Jobke < hey @ morrisjobke . de >
* @ author Robin Appelman < icewind @ owncloud . com >
* @ author Robin McCorkell < rmccorkell @ karoshi . org . uk >
* @ author Sam Tuke < mail @ samtuke . com >
* @ author Thomas Müller < thomas . mueller @ tmit . eu >
* @ author Vincent Petry < pvince81 @ owncloud . com >
2015-02-26 13:37:37 +03:00
*
2015-03-26 13:44:34 +03:00
* @ copyright Copyright ( c ) 2015 , ownCloud , Inc .
* @ license AGPL - 3.0
2012-07-11 20:51:27 +04:00
*
2015-03-26 13:44:34 +03:00
* This code is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License , version 3 ,
* as published by the Free Software Foundation .
2012-07-11 20:51:27 +04:00
*
2015-03-26 13:44:34 +03:00
* This program is distributed in the hope that it will be useful ,
2012-07-11 20:51:27 +04:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2015-03-26 13:44:34 +03:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
2012-07-11 20:51:27 +04:00
*
2015-03-26 13:44:34 +03:00
* You should have received a copy of the GNU Affero General Public License , version 3 ,
* along with this program . If not , see < http :// www . gnu . org / licenses />
2012-07-11 20:51:27 +04:00
*
*/
2015-02-26 13:37:37 +03:00
2014-12-03 12:57:16 +03:00
namespace OCA\Files_Encryption ;
2012-07-11 20:51:27 +04:00
/**
2014-05-19 19:50:53 +04:00
* Class for utilities relating to encrypted file storage system
2014-05-12 18:30:39 +04:00
* @ param \OC\Files\View $view expected to have OC '/' as root path
2013-01-31 23:40:51 +04:00
* @ param string $userId ID of the logged in user
* @ param int $client indicating status of client side encryption . Currently
2012-07-25 18:33:25 +04:00
* unused , likely to become obsolete shortly
2012-07-11 20:51:27 +04:00
*/
2013-05-27 14:41:55 +04:00
class Util {
2013-05-20 03:24:36 +04:00
2013-06-11 15:07:39 +04:00
const MIGRATION_COMPLETED = 1 ; // migration to new encryption completed
const MIGRATION_IN_PROGRESS = - 1 ; // migration is running
const MIGRATION_OPEN = 0 ; // user still needs to be migrated
2015-01-08 22:57:49 +03:00
const FILE_TYPE_FILE = 0 ;
const FILE_TYPE_VERSION = 1 ;
const FILE_TYPE_CACHE = 2 ;
2015-01-19 15:01:52 +03:00
/**
* @ var \OC\Files\View
*/
2014-05-12 18:30:39 +04:00
private $view ; // OC\Files\View object for filesystem operations
2015-01-19 15:01:52 +03:00
/**
* @ var string
*/
2013-11-20 21:10:56 +04:00
private $userId ; // ID of the user we use to encrypt/decrypt files
2015-01-19 15:01:52 +03:00
/**
* @ var string
*/
2013-11-21 13:09:07 +04:00
private $keyId ; // ID of the key we want to manipulate
2015-01-19 15:01:52 +03:00
/**
* @ var bool
*/
2012-07-11 20:51:27 +04:00
private $client ; // Client side encryption mode flag
2015-01-19 15:01:52 +03:00
/**
* @ var string
*/
2013-01-14 19:39:04 +04:00
private $publicKeyDir ; // Dir containing all public user keys
2015-01-19 15:01:52 +03:00
/**
* @ var string
*/
2013-01-14 19:39:04 +04:00
private $encryptionDir ; // Dir containing user's files_encryption
2015-01-19 15:01:52 +03:00
/**
* @ var string
*/
2014-11-10 14:40:24 +03:00
private $keysPath ; // Dir containing all file related encryption keys
2015-01-19 15:01:52 +03:00
/**
* @ var string
*/
2012-11-16 22:31:37 +04:00
private $publicKeyPath ; // Path to user's public key
2015-01-19 15:01:52 +03:00
/**
* @ var string
*/
2012-11-16 22:31:37 +04:00
private $privateKeyPath ; // Path to user's private key
2015-01-19 15:01:52 +03:00
/**
* @ var string
*/
2014-11-10 14:40:24 +03:00
private $userFilesDir ;
2015-01-19 15:01:52 +03:00
/**
* @ var string
*/
2013-05-13 17:15:35 +04:00
private $publicShareKeyId ;
2015-01-19 15:01:52 +03:00
/**
* @ var string
*/
2013-05-13 19:40:57 +04:00
private $recoveryKeyId ;
2015-01-19 15:01:52 +03:00
/**
* @ var bool
*/
2013-05-20 03:24:36 +04:00
private $isPublic ;
2012-07-11 20:51:27 +04:00
2013-05-20 03:24:36 +04:00
/**
2014-05-12 18:30:39 +04:00
* @ param \OC\Files\View $view
2014-05-13 15:29:25 +04:00
* @ param string $userId
2013-05-20 03:24:36 +04:00
* @ param bool $client
*/
2014-01-31 23:39:52 +04:00
public function __construct ( $view , $userId , $client = false ) {
2013-05-13 23:24:59 +04:00
2012-07-11 20:51:27 +04:00
$this -> view = $view ;
$this -> client = $client ;
2013-11-21 13:09:07 +04:00
$this -> userId = $userId ;
2013-05-20 03:24:36 +04:00
2014-02-13 19:28:49 +04:00
$appConfig = \OC :: $server -> getAppConfig ();
$this -> publicShareKeyId = $appConfig -> getValue ( 'files_encryption' , 'publicShareKeyId' );
$this -> recoveryKeyId = $appConfig -> getValue ( 'files_encryption' , 'recoveryKeyId' );
2013-05-20 03:24:36 +04:00
2013-11-21 13:09:07 +04:00
$this -> userDir = '/' . $this -> userId ;
2013-11-20 21:10:56 +04:00
$this -> fileFolderName = 'files' ;
$this -> userFilesDir =
'/' . $userId . '/' . $this -> fileFolderName ; // TODO: Does this need to be user configurable?
2014-11-18 19:25:36 +03:00
$this -> publicKeyDir = Keymanager :: getPublicKeyPath ();
2013-11-21 13:09:07 +04:00
$this -> encryptionDir = '/' . $this -> userId . '/' . 'files_encryption' ;
2014-11-10 14:40:24 +03:00
$this -> keysPath = $this -> encryptionDir . '/' . 'keys' ;
2013-11-20 21:10:56 +04:00
$this -> publicKeyPath =
2014-11-14 19:30:38 +03:00
$this -> publicKeyDir . '/' . $this -> userId . '.publicKey' ; // e.g. data/public-keys/admin.publicKey
2013-11-20 21:10:56 +04:00
$this -> privateKeyPath =
2014-11-14 19:30:38 +03:00
$this -> encryptionDir . '/' . $this -> userId . '.privateKey' ; // e.g. data/admin/admin.privateKey
2013-11-20 21:10:56 +04:00
// make sure that the owners home is mounted
\OC\Files\Filesystem :: initMountPoints ( $userId );
2014-12-03 18:52:44 +03:00
if ( Helper :: isPublicAccess ()) {
2013-11-21 13:09:07 +04:00
$this -> keyId = $this -> publicShareKeyId ;
2013-11-20 21:10:56 +04:00
$this -> isPublic = true ;
2013-05-20 03:24:36 +04:00
} else {
2013-11-21 13:09:07 +04:00
$this -> keyId = $this -> userId ;
2013-11-20 21:10:56 +04:00
$this -> isPublic = false ;
2013-05-20 03:24:36 +04:00
}
2012-07-11 20:51:27 +04:00
}
2013-05-20 03:24:36 +04:00
/**
* @ return bool
*/
2013-05-24 01:56:31 +04:00
public function ready () {
2013-05-20 03:24:36 +04:00
if (
2013-05-27 14:41:55 +04:00
! $this -> view -> file_exists ( $this -> encryptionDir )
2014-11-10 14:40:24 +03:00
or ! $this -> view -> file_exists ( $this -> keysPath )
2013-05-27 14:41:55 +04:00
or ! $this -> view -> file_exists ( $this -> publicKeyPath )
or ! $this -> view -> file_exists ( $this -> privateKeyPath )
2012-07-11 20:51:27 +04:00
) {
return false ;
} else {
return true ;
}
2013-12-05 21:49:55 +04:00
}
2013-05-20 03:24:36 +04:00
2013-12-05 21:49:55 +04:00
/**
2014-05-19 19:50:53 +04:00
* check if the users private & public key exists
2013-12-05 21:49:55 +04:00
* @ return boolean
*/
public function userKeysExists () {
if (
$this -> view -> file_exists ( $this -> privateKeyPath ) &&
$this -> view -> file_exists ( $this -> publicKeyPath )) {
return true ;
} else {
return false ;
}
2012-07-11 20:51:27 +04:00
}
2013-05-20 03:24:36 +04:00
2014-10-29 14:45:13 +03:00
/**
* create a new public / private key pair for the user
*
* @ param string $password password for the private key
*/
public function replaceUserKeys ( $password ) {
$this -> backupAllKeys ( 'password_reset' );
$this -> view -> unlink ( $this -> publicKeyPath );
$this -> view -> unlink ( $this -> privateKeyPath );
$this -> setupServerSide ( $password );
}
2013-05-20 03:24:36 +04:00
/**
2014-05-19 19:50:53 +04:00
* Sets up user folders and keys for serverside encryption
2013-05-30 03:13:22 +04:00
*
* @ param string $passphrase to encrypt server - stored private key with
* @ return bool
2013-05-20 03:24:36 +04:00
*/
2013-05-27 14:41:55 +04:00
public function setupServerSide ( $passphrase = null ) {
2013-05-20 03:24:36 +04:00
2013-03-19 22:53:15 +04:00
// Set directories to check / create
2013-05-20 03:24:36 +04:00
$setUpDirs = array (
2013-05-27 14:41:55 +04:00
$this -> userDir ,
$this -> publicKeyDir ,
$this -> encryptionDir ,
2014-11-10 14:40:24 +03:00
$this -> keysPath
2013-03-19 22:53:15 +04:00
);
2013-05-20 03:24:36 +04:00
2013-03-19 22:53:15 +04:00
// Check / create all necessary dirs
2013-05-27 14:41:55 +04:00
foreach ( $setUpDirs as $dirPath ) {
2013-05-20 03:24:36 +04:00
2013-05-27 14:41:55 +04:00
if ( ! $this -> view -> file_exists ( $dirPath )) {
2013-05-20 03:24:36 +04:00
2013-05-27 14:41:55 +04:00
$this -> view -> mkdir ( $dirPath );
2013-05-20 03:24:36 +04:00
2013-03-19 22:53:15 +04:00
}
2013-05-20 03:24:36 +04:00
2013-01-14 19:39:04 +04:00
}
2013-05-20 03:24:36 +04:00
2012-07-11 20:51:27 +04:00
// Create user keypair
2013-05-20 23:22:03 +04:00
// we should never override a keyfile
2013-05-20 03:24:36 +04:00
if (
2013-05-27 14:41:55 +04:00
! $this -> view -> file_exists ( $this -> publicKeyPath )
&& ! $this -> view -> file_exists ( $this -> privateKeyPath )
2012-07-11 20:51:27 +04:00
) {
2013-05-20 03:24:36 +04:00
2012-07-11 20:51:27 +04:00
// Generate keypair
$keypair = Crypt :: createKeypair ();
2013-05-20 03:24:36 +04:00
2013-06-19 17:52:33 +04:00
if ( $keypair ) {
2013-05-20 03:24:36 +04:00
2013-06-19 17:52:33 +04:00
\OC_FileProxy :: $enabled = false ;
2013-05-20 03:24:36 +04:00
2013-06-19 17:52:33 +04:00
// Encrypt private key with user pwd as passphrase
2014-07-21 15:02:28 +04:00
$encryptedPrivateKey = Crypt :: symmetricEncryptFileContent ( $keypair [ 'privateKey' ], $passphrase , Helper :: getCipher ());
2013-05-20 03:24:36 +04:00
2013-06-19 17:52:33 +04:00
// Save key-pair
if ( $encryptedPrivateKey ) {
2014-07-21 15:02:28 +04:00
$header = crypt :: generateHeader ();
$this -> view -> file_put_contents ( $this -> privateKeyPath , $header . $encryptedPrivateKey );
2013-06-19 17:52:33 +04:00
$this -> view -> file_put_contents ( $this -> publicKeyPath , $keypair [ 'publicKey' ]);
}
2013-05-20 03:24:36 +04:00
2013-06-19 17:52:33 +04:00
\OC_FileProxy :: $enabled = true ;
}
2013-05-20 03:24:36 +04:00
2013-05-20 23:22:03 +04:00
} else {
// check if public-key exists but private-key is missing
2013-05-27 14:41:55 +04:00
if ( $this -> view -> file_exists ( $this -> publicKeyPath ) && ! $this -> view -> file_exists ( $this -> privateKeyPath )) {
\OCP\Util :: writeLog ( 'Encryption library' ,
2013-11-21 13:09:07 +04:00
'public key exists but private key is missing for "' . $this -> keyId . '"' , \OCP\Util :: FATAL );
2013-05-20 23:22:03 +04:00
return false ;
2013-05-27 14:41:55 +04:00
} else {
if ( ! $this -> view -> file_exists ( $this -> publicKeyPath ) && $this -> view -> file_exists ( $this -> privateKeyPath )
) {
\OCP\Util :: writeLog ( 'Encryption library' ,
2013-11-21 13:09:07 +04:00
'private key exists but public key is missing for "' . $this -> keyId . '"' , \OCP\Util :: FATAL );
2013-05-27 14:41:55 +04:00
return false ;
}
2013-05-20 23:22:03 +04:00
}
2013-05-01 21:18:31 +04:00
}
2013-05-20 03:24:36 +04:00
2012-11-16 22:31:37 +04:00
return true ;
2013-05-20 03:24:36 +04:00
2012-07-11 20:51:27 +04:00
}
2013-05-13 17:15:35 +04:00
2013-05-20 03:24:36 +04:00
/**
* @ return string
*/
2013-05-24 01:56:31 +04:00
public function getPublicShareKeyId () {
2013-05-13 17:15:35 +04:00
return $this -> publicShareKeyId ;
}
2013-05-20 03:24:36 +04:00
2013-03-20 22:26:59 +04:00
/**
2014-05-19 19:50:53 +04:00
* Check whether pwd recovery is enabled for a given user
2013-05-20 03:24:36 +04: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 22:26:59 +04:00
* at the start of the uid in db
*/
2013-05-24 01:56:31 +04:00
public function recoveryEnabledForUser () {
2013-05-20 03:24:36 +04:00
2014-12-04 18:48:07 +03:00
$recoveryMode = \OC :: $server -> getConfig () -> getUserValue ( $this -> userId , 'files_encryption' , 'recovery_enabled' , '0' );
2013-05-20 03:24:36 +04:00
2014-05-06 21:20:49 +04:00
return ( $recoveryMode === '1' ) ? true : false ;
2013-05-20 03:24:36 +04:00
2013-03-20 22:26:59 +04:00
}
2013-05-20 03:24:36 +04:00
2013-03-20 22:26:59 +04:00
/**
2014-05-19 19:50:53 +04:00
* Enable / disable pwd recovery for a given user
2013-03-20 22:26:59 +04:00
* @ param bool $enabled Whether to enable or disable recovery
* @ return bool
*/
2013-05-27 14:41:55 +04:00
public function setRecoveryForUser ( $enabled ) {
2013-05-20 03:24:36 +04:00
2014-05-06 21:20:49 +04:00
$value = $enabled ? '1' : '0' ;
2014-12-04 18:48:07 +03:00
try {
\OC :: $server -> getConfig () -> setUserValue ( $this -> userId , 'files_encryption' , 'recovery_enabled' , $value );
return true ;
} catch ( \OCP\PreConditionNotMetException $e ) {
return false ;
}
2013-05-20 03:24:36 +04:00
2013-03-19 22:53:15 +04:00
}
2013-05-20 03:24:36 +04:00
2013-01-23 23:24:26 +04:00
/**
2014-05-19 19:50:53 +04:00
* Find all files and their encryption status within a directory
2013-01-23 23:24:26 +04:00
* @ param string $directory The path of the parent directory to search
2013-05-27 14:41:55 +04:00
* @ param bool $found the founded files if called again
2014-07-08 15:07:05 +04:00
* @ return array keys : plain , encrypted , broken
2013-01-29 23:54:40 +04: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 23:24:26 +04:00
*/
2013-05-27 14:41:55 +04:00
public function findEncFiles ( $directory , & $found = false ) {
2013-05-20 03:24:36 +04:00
2013-01-23 23:24:26 +04:00
// Disable proxy - we don't want files to be decrypted before
// we handle them
\OC_FileProxy :: $enabled = false ;
2013-05-20 03:24:36 +04:00
2013-05-27 22:44:38 +04:00
if ( $found === false ) {
2013-05-27 14:41:55 +04:00
$found = array (
'plain' => array (),
'encrypted' => array (),
2014-02-10 20:23:54 +04:00
'broken' => array (),
2013-05-27 14:41:55 +04:00
);
2013-05-22 02:53:07 +04:00
}
2013-05-20 03:24:36 +04:00
2014-05-22 17:43:42 +04:00
if ( $this -> view -> is_dir ( $directory ) && $handle = $this -> view -> opendir ( $directory )){
if ( is_resource ( $handle )) {
2013-09-05 13:58:57 +04:00
while ( false !== ( $file = readdir ( $handle ))) {
2014-02-10 20:23:54 +04:00
if ( $file !== " . " && $file !== " .. " ) {
2015-01-19 15:01:37 +03:00
// skip stray part files
if ( Helper :: isPartialFilePath ( $file )) {
continue ;
}
2013-09-05 13:58:57 +04:00
$filePath = $directory . '/' . $this -> view -> getRelativePath ( '/' . $file );
2014-12-03 18:52:44 +03:00
$relPath = Helper :: stripUserFilesPath ( $filePath );
2013-09-05 13:58:57 +04:00
// If the path is a directory, search
// its contents
if ( $this -> view -> is_dir ( $filePath )) {
$this -> findEncFiles ( $filePath , $found );
// If the path is a file, determine
// its encryption status
} elseif ( $this -> view -> is_file ( $filePath )) {
// Disable proxies again, some-
// where they got re-enabled :/
\OC_FileProxy :: $enabled = false ;
$isEncryptedPath = $this -> isEncryptedPath ( $filePath );
// If the file is encrypted
// NOTE: If the userId is
// empty or not set, file will
// detected as plain
// NOTE: This is inefficient;
// scanning every file like this
// will eat server resources :(
2014-02-10 20:23:54 +04:00
if ( $isEncryptedPath ) {
$fileKey = Keymanager :: getFileKey ( $this -> view , $this , $relPath );
$shareKey = Keymanager :: getShareKey ( $this -> view , $this -> userId , $this , $relPath );
// if file is encrypted but now file key is available, throw exception
if ( $fileKey === false || $shareKey === false ) {
\OCP\Util :: writeLog ( 'encryption library' , 'No keys available to decrypt the file: ' . $filePath , \OCP\Util :: ERROR );
$found [ 'broken' ][] = array (
'name' => $file ,
'path' => $filePath ,
);
} else {
$found [ 'encrypted' ][] = array (
'name' => $file ,
'path' => $filePath ,
);
}
2013-09-05 13:58:57 +04:00
// If the file is not encrypted
} else {
$found [ 'plain' ][] = array (
'name' => $file ,
'path' => $relPath
);
}
2012-07-31 22:28:11 +04:00
}
}
}
}
}
2013-05-20 03:24:36 +04:00
2013-01-23 23:24:26 +04:00
\OC_FileProxy :: $enabled = true ;
2013-05-20 03:24:36 +04:00
2014-05-22 17:43:42 +04:00
return $found ;
2012-07-31 22:28:11 +04:00
}
2013-05-20 03:24:36 +04:00
2013-05-09 20:16:59 +04:00
/**
2014-05-19 19:50:53 +04:00
* Check if a given path identifies an encrypted file
2013-05-30 03:13:22 +04:00
* @ param string $path
2013-05-20 03:24:36 +04:00
* @ return boolean
*/
2013-05-27 14:41:55 +04:00
public function isEncryptedPath ( $path ) {
2013-05-20 03:24:36 +04:00
2013-12-17 18:53:25 +04:00
// Disable encryption proxy so data retrieved is in its
// original form
$proxyStatus = \OC_FileProxy :: $enabled ;
\OC_FileProxy :: $enabled = false ;
2013-05-15 00:32:39 +04:00
2013-12-17 18:53:25 +04:00
$data = '' ;
2014-03-27 16:49:48 +04:00
// we only need 24 byte from the last chunk
if ( $this -> view -> file_exists ( $path )) {
$handle = $this -> view -> fopen ( $path , 'r' );
if ( is_resource ( $handle )) {
// suppress fseek warining, we handle the case that fseek doesn't
// work in the else branch
if ( @ fseek ( $handle , - 24 , SEEK_END ) === 0 ) {
2013-12-17 19:18:05 +04:00
$data = fgets ( $handle );
2014-03-27 16:49:48 +04:00
} else {
// if fseek failed on the storage we create a local copy from the file
// and read this one
fclose ( $handle );
$localFile = $this -> view -> getLocalFile ( $path );
$handle = fopen ( $localFile , 'r' );
if ( is_resource ( $handle ) && fseek ( $handle , - 24 , SEEK_END ) === 0 ) {
$data = fgets ( $handle );
}
2013-12-17 19:18:05 +04:00
}
2014-03-27 16:49:48 +04:00
fclose ( $handle );
2013-12-17 19:18:05 +04:00
}
2013-11-20 14:02:22 +04:00
}
2013-05-20 03:24:36 +04:00
2013-12-17 18:53:25 +04:00
// re-enable proxy
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-05-20 03:24:36 +04:00
2013-12-17 18:53:25 +04:00
return Crypt :: isCatfileContent ( $data );
2012-10-17 19:35:19 +04:00
}
2013-04-27 02:05:20 +04:00
2013-05-09 20:16:59 +04:00
/**
2014-05-19 19:50:53 +04:00
* get the file size of the unencrypted file
2013-05-20 03:24:36 +04:00
* @ param string $path absolute path
* @ return bool
*/
2013-05-27 14:41:55 +04:00
public function getFileSize ( $path ) {
2013-04-27 02:05:20 +04:00
2013-05-09 20:16:59 +04:00
$result = 0 ;
2013-04-27 02:05:20 +04:00
2013-05-09 20:16:59 +04:00
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy :: $enabled ;
\OC_FileProxy :: $enabled = false ;
2013-04-27 02:05:20 +04:00
2013-05-30 03:13:22 +04:00
// split the path parts
$pathParts = explode ( '/' , $path );
2013-06-03 20:42:13 +04:00
if ( isset ( $pathParts [ 2 ]) && $pathParts [ 2 ] === 'files' && $this -> view -> file_exists ( $path )
&& $this -> isEncryptedPath ( $path )
) {
2013-04-27 02:05:20 +04:00
2014-10-28 21:19:10 +03:00
$cipher = 'AES-128-CFB' ;
2014-10-20 00:27:15 +04:00
$realSize = 0 ;
// get the size from filesystem
$size = $this -> view -> filesize ( $path );
// open stream
2014-10-20 02:28:41 +04:00
$stream = $this -> view -> fopen ( $path , " r " );
2014-07-21 15:02:28 +04:00
2014-10-20 02:28:41 +04:00
if ( is_resource ( $stream )) {
// if the file contains a encryption header we
// we set the cipher
// and we update the size
if ( $this -> containHeader ( $path )) {
$data = fread ( $stream , Crypt :: BLOCKSIZE );
$header = Crypt :: parseHeader ( $data );
$cipher = Crypt :: getCipher ( $header );
$size -= Crypt :: BLOCKSIZE ;
}
// fast path, else the calculation for $lastChunkNr is bogus
if ( $size === 0 ) {
\OC_FileProxy :: $enabled = $proxyStatus ;
return 0 ;
}
2013-10-05 20:00:46 +04:00
2014-10-20 02:28:41 +04:00
// calculate last chunk nr
// next highest is end of chunks, one subtracted is last one
// we have to read the last chunk, we can't just calculate it (because of padding etc)
$lastChunkNr = ceil ( $size / Crypt :: BLOCKSIZE ) - 1 ;
2013-04-27 02:05:20 +04:00
2013-05-09 20:16:59 +04:00
// calculate last chunk position
2014-10-20 00:27:15 +04:00
$lastChunkPos = ( $lastChunkNr * Crypt :: BLOCKSIZE );
2013-04-27 02:05:20 +04:00
2014-10-20 00:27:15 +04:00
// get the content of the last chunk
if ( @ fseek ( $stream , $lastChunkPos , SEEK_CUR ) === 0 ) {
$realSize += $lastChunkNr * 6126 ;
2014-10-20 02:28:41 +04:00
}
$lastChunkContentEncrypted = '' ;
$count = Crypt :: BLOCKSIZE ;
while ( $count > 0 ) {
$data = fread ( $stream , Crypt :: BLOCKSIZE );
$count = strlen ( $data );
$lastChunkContentEncrypted .= $data ;
if ( strlen ( $lastChunkContentEncrypted ) > Crypt :: BLOCKSIZE ) {
$realSize += 6126 ;
$lastChunkContentEncrypted = substr ( $lastChunkContentEncrypted , Crypt :: BLOCKSIZE );
2013-12-17 21:13:46 +04:00
}
}
2014-10-21 01:04:11 +04:00
fclose ( $stream );
2014-12-03 18:52:44 +03:00
$relPath = Helper :: stripUserFilesPath ( $path );
2014-10-21 01:04:11 +04:00
$shareKey = Keymanager :: getShareKey ( $this -> view , $this -> keyId , $this , $relPath );
if ( $shareKey === false ) {
2014-10-21 01:25:54 +04:00
\OC_FileProxy :: $enabled = $proxyStatus ;
2014-10-21 01:04:11 +04:00
return $result ;
}
2014-12-03 18:52:44 +03:00
$session = new Session ( $this -> view );
2014-10-20 00:27:15 +04:00
$privateKey = $session -> getPrivateKey ();
2014-10-20 02:28:41 +04:00
$plainKeyfile = $this -> decryptKeyfile ( $relPath , $privateKey );
2014-10-20 00:27:15 +04:00
$plainKey = Crypt :: multiKeyDecrypt ( $plainKeyfile , $shareKey , $privateKey );
$lastChunkContent = Crypt :: symmetricDecryptFileContent ( $lastChunkContentEncrypted , $plainKey , $cipher );
2013-04-27 02:05:20 +04:00
2013-05-09 20:16:59 +04:00
// calc the real file size with the size of the last chunk
2014-10-20 00:27:15 +04:00
$realSize += strlen ( $lastChunkContent );
2013-04-27 02:05:20 +04:00
2013-05-09 20:16:59 +04:00
// store file size
$result = $realSize ;
}
}
2013-04-28 01:34:25 +04:00
2013-05-09 20:16:59 +04:00
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-04-28 01:34:25 +04:00
2013-05-09 20:16:59 +04:00
return $result ;
}
2013-05-20 03:24:36 +04:00
2014-07-21 15:02:28 +04:00
/**
* check if encrypted file contain a encryption header
*
* @ param string $path
* @ return boolean
*/
private function containHeader ( $path ) {
// Disable encryption proxy to read the raw data
$proxyStatus = \OC_FileProxy :: $enabled ;
\OC_FileProxy :: $enabled = false ;
$isHeader = false ;
$handle = $this -> view -> fopen ( $path , 'r' );
if ( is_resource ( $handle )) {
$firstBlock = fread ( $handle , Crypt :: BLOCKSIZE );
$isHeader = Crypt :: isHeader ( $firstBlock );
}
\OC_FileProxy :: $enabled = $proxyStatus ;
return $isHeader ;
}
2013-05-09 20:16:59 +04:00
/**
2014-05-19 19:50:53 +04:00
* fix the file size of the encrypted file
2013-05-27 14:41:55 +04:00
* @ param string $path absolute path
* @ return boolean true / false if file is encrypted
2013-05-09 20:16:59 +04:00
*/
2013-05-27 14:41:55 +04:00
public function fixFileSize ( $path ) {
2013-04-28 01:34:25 +04:00
2013-05-09 20:16:59 +04:00
$result = false ;
2013-04-28 01:34:25 +04:00
2013-05-09 20:16:59 +04:00
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy :: $enabled ;
\OC_FileProxy :: $enabled = false ;
2013-04-28 01:34:25 +04:00
2013-05-27 14:41:55 +04:00
$realSize = $this -> getFileSize ( $path );
2013-05-20 03:24:36 +04:00
2013-05-27 14:41:55 +04:00
if ( $realSize > 0 ) {
2013-05-20 03:24:36 +04:00
2013-05-27 14:41:55 +04:00
$cached = $this -> view -> getFileInfo ( $path );
2013-05-11 02:23:30 +04:00
$cached [ 'encrypted' ] = true ;
2013-04-28 01:34:25 +04:00
2013-05-09 20:16:59 +04:00
// set the size
$cached [ 'unencrypted_size' ] = $realSize ;
2013-04-27 02:05:20 +04:00
2013-05-09 20:16:59 +04:00
// put file info
2013-05-27 14:41:55 +04:00
$this -> view -> putFileInfo ( $path , $cached );
2013-04-27 02:05:20 +04:00
2013-05-09 20:16:59 +04:00
$result = true ;
2013-05-20 03:24:36 +04:00
2013-05-09 20:16:59 +04:00
}
2013-04-27 02:05:20 +04:00
2013-05-09 20:16:59 +04:00
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-04-27 02:05:20 +04:00
2013-05-09 20:16:59 +04:00
return $result ;
}
2013-04-27 02:05:20 +04:00
2013-07-30 14:19:04 +04:00
/**
2014-05-19 19:50:53 +04:00
* encrypt versions from given file
2013-07-30 14:19:04 +04:00
* @ param array $filelist list of encrypted files , relative to data / user / files
* @ return boolean
*/
private function encryptVersions ( $filelist ) {
$successful = true ;
if ( \OCP\App :: isEnabled ( 'files_versions' )) {
foreach ( $filelist as $filename ) {
$versions = \OCA\Files_Versions\Storage :: getVersions ( $this -> userId , $filename );
foreach ( $versions as $version ) {
$path = '/' . $this -> userId . '/files_versions/' . $version [ 'path' ] . '.v' . $version [ 'version' ];
$encHandle = fopen ( 'crypt://' . $path . '.part' , 'wb' );
if ( $encHandle === false ) {
\OCP\Util :: writeLog ( 'Encryption library' , 'couldn\'t open "' . $path . '", decryption failed!' , \OCP\Util :: FATAL );
$successful = false ;
continue ;
}
$plainHandle = $this -> view -> fopen ( $path , 'rb' );
if ( $plainHandle === false ) {
\OCP\Util :: writeLog ( 'Encryption library' , 'couldn\'t open "' . $path . '.part", decryption failed!' , \OCP\Util :: FATAL );
$successful = false ;
continue ;
}
stream_copy_to_stream ( $plainHandle , $encHandle );
fclose ( $encHandle );
fclose ( $plainHandle );
$this -> view -> rename ( $path . '.part' , $path );
}
}
}
return $successful ;
}
/**
2014-05-19 19:50:53 +04:00
* decrypt versions from given file
2013-07-30 14:19:04 +04:00
* @ param string $filelist list of decrypted files , relative to data / user / files
* @ return boolean
*/
private function decryptVersions ( $filelist ) {
$successful = true ;
if ( \OCP\App :: isEnabled ( 'files_versions' )) {
foreach ( $filelist as $filename ) {
$versions = \OCA\Files_Versions\Storage :: getVersions ( $this -> userId , $filename );
foreach ( $versions as $version ) {
$path = '/' . $this -> userId . '/files_versions/' . $version [ 'path' ] . '.v' . $version [ 'version' ];
$encHandle = fopen ( 'crypt://' . $path , 'rb' );
if ( $encHandle === false ) {
\OCP\Util :: writeLog ( 'Encryption library' , 'couldn\'t open "' . $path . '", decryption failed!' , \OCP\Util :: FATAL );
$successful = false ;
continue ;
}
$plainHandle = $this -> view -> fopen ( $path . '.part' , 'wb' );
if ( $plainHandle === false ) {
\OCP\Util :: writeLog ( 'Encryption library' , 'couldn\'t open "' . $path . '.part", decryption failed!' , \OCP\Util :: FATAL );
$successful = false ;
continue ;
}
stream_copy_to_stream ( $encHandle , $plainHandle );
fclose ( $encHandle );
fclose ( $plainHandle );
$this -> view -> rename ( $path . '.part' , $path );
}
}
}
return $successful ;
}
2013-08-18 13:21:01 +04:00
2013-07-29 19:06:05 +04:00
/**
2014-05-19 19:50:53 +04:00
* Decrypt all files
2013-07-29 19:06:05 +04:00
* @ return bool
*/
public function decryptAll () {
$found = $this -> findEncFiles ( $this -> userId . '/files' );
2013-07-30 11:48:30 +04:00
$successful = true ;
2013-07-29 19:06:05 +04:00
if ( $found ) {
2013-07-30 14:19:04 +04:00
$versionStatus = \OCP\App :: isEnabled ( 'files_versions' );
\OC_App :: disable ( 'files_versions' );
2013-08-18 13:21:01 +04:00
2013-08-12 15:59:49 +04:00
$decryptedFiles = array ();
2013-07-30 14:19:04 +04:00
2013-07-29 19:06:05 +04:00
// Encrypt unencrypted files
foreach ( $found [ 'encrypted' ] as $encryptedFile ) {
//relative to data/<user>/file
$relPath = Helper :: stripUserFilesPath ( $encryptedFile [ 'path' ]);
2013-11-12 18:51:51 +04:00
//get file info
$fileInfo = \OC\Files\Filesystem :: getFileInfo ( $relPath );
2013-07-29 19:06:05 +04:00
//relative to /data
$rawPath = $encryptedFile [ 'path' ];
2013-08-18 13:21:01 +04:00
2013-08-12 16:30:43 +04:00
//get timestamp
2013-11-12 18:51:51 +04:00
$timestamp = $fileInfo [ 'mtime' ];
2013-08-18 13:21:01 +04:00
2013-07-31 18:35:14 +04:00
//enable proxy to use OC\Files\View to access the original file
\OC_FileProxy :: $enabled = true ;
2013-07-29 19:06:05 +04:00
// Open enc file handle for binary reading
2013-07-31 18:35:14 +04:00
$encHandle = $this -> view -> fopen ( $rawPath , 'rb' );
// Disable proxy to prevent file being encrypted again
\OC_FileProxy :: $enabled = false ;
2013-07-29 19:06:05 +04:00
2013-07-30 11:48:30 +04:00
if ( $encHandle === false ) {
\OCP\Util :: writeLog ( 'Encryption library' , 'couldn\'t open "' . $rawPath . '", decryption failed!' , \OCP\Util :: FATAL );
$successful = false ;
continue ;
}
2013-07-29 19:06:05 +04:00
// Open plain file handle for binary writing, with same filename as original plain file
$plainHandle = $this -> view -> fopen ( $rawPath . '.part' , 'wb' );
2013-07-30 11:48:30 +04:00
if ( $plainHandle === false ) {
\OCP\Util :: writeLog ( 'Encryption library' , 'couldn\'t open "' . $rawPath . '.part", decryption failed!' , \OCP\Util :: FATAL );
$successful = false ;
continue ;
}
2013-07-29 19:06:05 +04:00
// Move plain file to a temporary location
$size = stream_copy_to_stream ( $encHandle , $plainHandle );
2013-07-30 11:48:30 +04:00
if ( $size === 0 ) {
\OCP\Util :: writeLog ( 'Encryption library' , 'Zero bytes copied of "' . $rawPath . '", decryption failed!' , \OCP\Util :: FATAL );
$successful = false ;
continue ;
}
2013-07-29 19:06:05 +04:00
fclose ( $encHandle );
fclose ( $plainHandle );
$fakeRoot = $this -> view -> getRoot ();
$this -> view -> chroot ( '/' . $this -> userId . '/files' );
$this -> view -> rename ( $relPath . '.part' , $relPath );
2013-08-12 16:30:43 +04:00
//set timestamp
2013-11-12 18:51:51 +04:00
$this -> view -> touch ( $relPath , $timestamp );
$this -> view -> chroot ( $fakeRoot );
2013-08-18 13:21:01 +04:00
2013-07-29 19:06:05 +04:00
// Add the file to the cache
\OC\Files\Filesystem :: putFileInfo ( $relPath , array (
'encrypted' => false ,
'size' => $size ,
2013-12-06 19:11:41 +04:00
'unencrypted_size' => 0 ,
2013-08-18 13:21:01 +04:00
'etag' => $fileInfo [ 'etag' ]
2013-07-29 19:06:05 +04:00
));
2013-08-18 13:21:01 +04:00
2013-07-30 14:19:04 +04:00
$decryptedFiles [] = $relPath ;
2013-07-29 19:06:05 +04:00
}
2013-07-30 14:19:04 +04:00
if ( $versionStatus ) {
\OC_App :: enable ( 'files_versions' );
}
2013-08-18 13:21:01 +04:00
2013-07-30 14:19:04 +04:00
if ( ! $this -> decryptVersions ( $decryptedFiles )) {
$successful = false ;
}
2013-08-18 13:21:01 +04:00
2014-02-10 20:23:54 +04:00
// if there are broken encrypted files than the complete decryption
// was not successful
if ( ! empty ( $found [ 'broken' ])) {
$successful = false ;
}
2013-07-30 11:48:30 +04:00
if ( $successful ) {
2015-01-13 14:45:33 +03:00
$this -> backupAllKeys ( 'decryptAll' , false , false );
2014-11-10 14:40:24 +03:00
$this -> view -> deleteAll ( $this -> keysPath );
2013-07-30 11:48:30 +04:00
}
2013-07-29 19:06:05 +04:00
\OC_FileProxy :: $enabled = true ;
}
2013-07-30 11:48:30 +04:00
return $successful ;
2013-07-29 19:06:05 +04:00
}
2013-05-20 03:24:36 +04:00
2013-01-23 23:24:26 +04:00
/**
2014-05-19 19:50:53 +04:00
* Encrypt all files in a directory
2013-01-23 23:24:26 +04:00
* @ param string $dirPath the directory whose files will be encrypted
2013-05-20 03:24:36 +04:00
* @ return bool
2013-01-23 23:24:26 +04:00
* @ note Encryption is recursive
*/
2014-07-08 15:07:05 +04:00
public function encryptAll ( $dirPath ) {
2013-05-17 16:13:05 +04:00
2014-05-22 17:43:42 +04:00
$result = true ;
2013-07-30 14:19:04 +04:00
2013-07-29 19:06:05 +04:00
$found = $this -> findEncFiles ( $dirPath );
2013-05-17 16:13:05 +04:00
2014-05-22 17:43:42 +04:00
// Disable proxy to prevent file being encrypted twice
\OC_FileProxy :: $enabled = false ;
2013-08-18 13:21:01 +04:00
2014-05-22 17:43:42 +04:00
$versionStatus = \OCP\App :: isEnabled ( 'files_versions' );
\OC_App :: disable ( 'files_versions' );
2013-08-18 13:21:01 +04:00
2014-05-22 17:43:42 +04:00
$encryptedFiles = array ();
2013-05-17 16:13:05 +04:00
2014-05-22 17:43:42 +04:00
// Encrypt unencrypted files
foreach ( $found [ 'plain' ] as $plainFile ) {
2013-05-17 16:13:05 +04:00
2014-05-22 17:43:42 +04:00
//get file info
$fileInfo = \OC\Files\Filesystem :: getFileInfo ( $plainFile [ 'path' ]);
2013-08-18 13:21:01 +04:00
2014-05-22 17:43:42 +04:00
//relative to data/<user>/file
$relPath = $plainFile [ 'path' ];
2013-08-18 13:21:01 +04:00
2014-05-22 17:43:42 +04:00
//relative to /data
$rawPath = '/' . $this -> userId . '/files/' . $plainFile [ 'path' ];
2013-05-17 16:13:05 +04:00
2014-05-22 17:43:42 +04:00
// keep timestamp
$timestamp = $fileInfo [ 'mtime' ];
2013-08-18 13:21:01 +04:00
2014-05-22 17:43:42 +04:00
// Open plain file handle for binary reading
$plainHandle = $this -> view -> fopen ( $rawPath , 'rb' );
2013-05-17 16:13:05 +04:00
2014-05-22 17:43:42 +04:00
// Open enc file handle for binary writing, with same filename as original plain file
$encHandle = fopen ( 'crypt://' . $rawPath . '.part' , 'wb' );
2013-05-17 16:13:05 +04:00
2014-05-22 17:43:42 +04:00
if ( is_resource ( $encHandle ) && is_resource ( $plainHandle )) {
// Move plain file to a temporary location
$size = stream_copy_to_stream ( $plainHandle , $encHandle );
2013-05-17 16:13:05 +04:00
2014-05-22 17:43:42 +04:00
fclose ( $encHandle );
fclose ( $plainHandle );
2013-05-17 16:13:05 +04:00
2014-05-22 17:43:42 +04:00
$fakeRoot = $this -> view -> getRoot ();
$this -> view -> chroot ( '/' . $this -> userId . '/files' );
2013-05-22 04:02:42 +04:00
2014-05-22 17:43:42 +04:00
$this -> view -> rename ( $relPath . '.part' , $relPath );
2013-05-17 16:13:05 +04:00
2014-05-22 17:43:42 +04:00
// set timestamp
$this -> view -> touch ( $relPath , $timestamp );
2013-11-12 18:51:51 +04:00
2014-05-22 17:43:42 +04:00
$encSize = $this -> view -> filesize ( $relPath );
2013-05-17 16:13:05 +04:00
2014-05-22 17:43:42 +04:00
$this -> view -> chroot ( $fakeRoot );
2013-08-18 13:21:01 +04:00
2014-05-22 17:43:42 +04:00
// Add the file to the cache
\OC\Files\Filesystem :: putFileInfo ( $relPath , array (
'encrypted' => true ,
'size' => $encSize ,
'unencrypted_size' => $size ,
'etag' => $fileInfo [ 'etag' ]
));
2013-07-30 14:19:04 +04:00
2014-05-22 17:43:42 +04:00
$encryptedFiles [] = $relPath ;
} else {
\OCP\Util :: writeLog ( 'files_encryption' , 'initial encryption: could not encrypt ' . $rawPath , \OCP\Util :: FATAL );
$result = false ;
2013-01-23 23:24:26 +04:00
}
2014-05-22 17:43:42 +04:00
}
2013-05-17 16:13:05 +04:00
2014-05-22 17:43:42 +04:00
\OC_FileProxy :: $enabled = true ;
2013-05-17 16:13:05 +04:00
2014-05-22 17:43:42 +04:00
if ( $versionStatus ) {
\OC_App :: enable ( 'files_versions' );
}
2013-08-18 13:21:01 +04:00
2014-05-22 17:43:42 +04:00
$result = $result && $this -> encryptVersions ( $encryptedFiles );
2013-08-18 13:21:01 +04:00
2014-05-22 17:43:42 +04:00
return $result ;
2013-05-17 16:13:05 +04:00
2012-07-31 22:28:11 +04:00
}
2013-05-17 16:13:05 +04:00
2013-02-06 18:30:40 +04:00
/**
2014-05-19 19:50:53 +04:00
* Return important encryption related paths
2013-02-06 18:30:40 +04:00
* @ param string $pathName Name of the directory to return the path of
* @ return string path
*/
2013-05-27 14:41:55 +04:00
public function getPath ( $pathName ) {
2013-05-20 03:24:36 +04:00
2013-05-27 14:41:55 +04:00
switch ( $pathName ) {
2013-05-20 03:24:36 +04:00
2012-11-16 22:31:37 +04:00
case 'publicKeyDir' :
2013-05-20 03:24:36 +04:00
2012-11-16 22:31:37 +04:00
return $this -> publicKeyDir ;
2013-05-20 03:24:36 +04:00
2012-11-16 22:31:37 +04:00
break ;
2013-05-20 03:24:36 +04:00
2012-11-16 22:31:37 +04:00
case 'encryptionDir' :
2013-05-20 03:24:36 +04:00
2012-11-16 22:31:37 +04:00
return $this -> encryptionDir ;
2013-05-20 03:24:36 +04:00
2012-11-16 22:31:37 +04:00
break ;
2013-05-20 03:24:36 +04:00
2014-11-10 14:40:24 +03:00
case 'keysPath' :
2013-05-20 03:24:36 +04:00
2014-11-10 14:40:24 +03:00
return $this -> keysPath ;
2013-05-20 03:24:36 +04:00
2012-11-16 22:31:37 +04:00
break ;
2013-05-20 03:24:36 +04:00
2012-11-16 22:31:37 +04:00
case 'publicKeyPath' :
2013-05-20 03:24:36 +04:00
2012-11-16 22:31:37 +04:00
return $this -> publicKeyPath ;
2013-05-20 03:24:36 +04:00
2012-11-16 22:31:37 +04:00
break ;
2013-05-20 03:24:36 +04:00
2012-11-16 22:31:37 +04:00
case 'privateKeyPath' :
2013-05-20 03:24:36 +04:00
2012-11-16 22:31:37 +04:00
return $this -> privateKeyPath ;
2013-05-20 03:24:36 +04:00
2012-11-16 22:31:37 +04:00
break ;
}
2013-05-20 03:24:36 +04:00
2013-05-24 01:56:31 +04:00
return false ;
2012-11-16 22:31:37 +04:00
}
2013-05-20 03:24:36 +04:00
2014-11-12 15:33:41 +03:00
/**
* Returns whether the given user is ready for encryption .
* Also returns true if the given user is the public user
* or the recovery key user .
*
* @ param string $user user to check
*
* @ return boolean true if the user is ready , false otherwise
*/
private function isUserReady ( $user ) {
if ( $user === $this -> publicShareKeyId
|| $user === $this -> recoveryKeyId
) {
return true ;
}
$util = new Util ( $this -> view , $user );
return $util -> ready ();
}
2013-02-19 23:16:50 +04:00
/**
2014-05-19 19:50:53 +04:00
* Filter an array of UIDs to return only ones ready for sharing
2013-02-19 23:16:50 +04:00
* @ param array $unfilteredUsers users to be checked for sharing readiness
2013-05-27 14:41:55 +04:00
* @ return array as multi - dimensional array . keys : ready , unready
2013-02-19 23:16:50 +04:00
*/
2013-05-27 14:41:55 +04:00
public function filterShareReadyUsers ( $unfilteredUsers ) {
2013-05-20 03:24:36 +04:00
2013-02-19 23:16:50 +04:00
// This array will collect the filtered IDs
2013-04-18 22:02:27 +04:00
$readyIds = $unreadyIds = array ();
2013-05-20 03:24:36 +04:00
2013-02-19 23:16:50 +04:00
// Loop through users and create array of UIDs that need new keyfiles
2013-05-27 14:41:55 +04:00
foreach ( $unfilteredUsers as $user ) {
2013-02-19 23:16:50 +04:00
// Check that the user is encryption capable, or is the
2014-11-12 15:33:41 +03:00
// public system user (for public shares)
if ( $this -> isUserReady ( $user )) {
2013-05-20 03:24:36 +04:00
2013-03-30 00:11:29 +04:00
// Construct array of ready UIDs for Keymanager{}
$readyIds [] = $user ;
2013-05-20 03:24:36 +04:00
2013-02-19 23:16:50 +04:00
} else {
2013-05-20 03:24:36 +04:00
2013-03-30 00:11:29 +04:00
// Construct array of unready UIDs for Keymanager{}
$unreadyIds [] = $user ;
2013-05-20 03:24:36 +04:00
2013-02-19 23:16:50 +04:00
// Log warning; we can't do necessary setup here
// because we don't have the user passphrase
2013-05-27 14:41:55 +04:00
\OCP\Util :: writeLog ( 'Encryption library' ,
'"' . $user . '" is not setup for encryption' , \OCP\Util :: WARN );
2013-05-20 03:24:36 +04:00
2013-02-19 23:16:50 +04:00
}
2013-05-20 03:24:36 +04:00
2013-02-19 23:16:50 +04:00
}
2013-05-20 03:24:36 +04:00
return array (
2013-05-24 01:56:31 +04:00
'ready' => $readyIds ,
'unready' => $unreadyIds
2013-03-30 00:11:29 +04:00
);
2013-05-20 03:24:36 +04:00
2013-02-19 23:16:50 +04:00
}
2013-05-20 03:24:36 +04:00
2013-02-20 23:18:00 +04:00
/**
2014-05-19 19:50:53 +04:00
* Decrypt a keyfile
2013-02-27 22:46:44 +04:00
* @ param string $filePath
* @ param string $privateKey
2014-02-06 19:30:58 +04:00
* @ return false | string
2013-02-20 23:18:00 +04:00
*/
2013-08-12 18:19:08 +04:00
private function decryptKeyfile ( $filePath , $privateKey ) {
2013-02-20 23:18:00 +04:00
// Get the encrypted keyfile
2013-11-21 01:44:23 +04:00
$encKeyfile = Keymanager :: getFileKey ( $this -> view , $this , $filePath );
2013-05-20 03:24:36 +04:00
2013-08-12 18:19:08 +04:00
// The file has a shareKey and must use it for decryption
2013-11-21 13:09:07 +04:00
$shareKey = Keymanager :: getShareKey ( $this -> view , $this -> keyId , $this , $filePath );
2013-05-20 03:24:36 +04:00
2013-08-12 18:19:08 +04:00
$plainKeyfile = Crypt :: multiKeyDecrypt ( $encKeyfile , $shareKey , $privateKey );
2013-05-20 03:24:36 +04:00
2013-02-27 22:46:44 +04:00
return $plainKeyfile ;
}
2013-05-20 03:24:36 +04:00
2013-02-27 22:46:44 +04:00
/**
2014-05-19 19:50:53 +04:00
* Encrypt keyfile to multiple users
2013-05-20 03:24:36 +04:00
* @ param Session $session
2013-02-27 22:46:44 +04: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-20 03:24:36 +04:00
* @ return bool
2013-02-27 22:46:44 +04:00
*/
2013-05-27 14:41:55 +04:00
public function setSharedFileKeyfiles ( Session $session , array $users , $filePath ) {
2013-05-20 03:24:36 +04:00
2013-02-27 22:46:44 +04:00
// Make sure users are capable of sharing
2013-05-27 14:41:55 +04:00
$filteredUids = $this -> filterShareReadyUsers ( $users );
2013-05-20 03:24:36 +04:00
2013-05-09 16:43:06 +04:00
// If we're attempting to share to unready users
2013-05-27 14:41:55 +04:00
if ( ! empty ( $filteredUids [ 'unready' ])) {
2013-05-20 03:24:36 +04:00
2013-05-27 14:41:55 +04: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-20 03:24:36 +04:00
2013-05-09 16:43:06 +04:00
return false ;
2013-05-20 03:24:36 +04:00
2013-03-30 00:11:29 +04:00
}
2013-05-20 03:24:36 +04:00
2013-02-27 22:46:44 +04:00
// Get public keys for each user, ready for generating sharekeys
2013-05-27 14:41:55 +04:00
$userPubKeys = Keymanager :: getPublicKeys ( $this -> view , $filteredUids [ 'ready' ]);
2013-05-20 03:24:36 +04:00
2013-05-09 16:43:06 +04:00
// Note proxy status then disable it
$proxyStatus = \OC_FileProxy :: $enabled ;
2013-02-27 22:46:44 +04:00
\OC_FileProxy :: $enabled = false ;
// Get the current users's private key for decrypting existing keyfile
$privateKey = $session -> getPrivateKey ();
2013-05-16 16:53:04 +04:00
2014-07-16 14:06:00 +04:00
try {
// Decrypt keyfile
$plainKeyfile = $this -> decryptKeyfile ( $filePath , $privateKey );
// Re-enc keyfile to (additional) sharekeys
$multiEncKey = Crypt :: multiKeyEncrypt ( $plainKeyfile , $userPubKeys );
2014-11-05 16:42:36 +03:00
} catch ( Exception\EncryptionException $e ) {
2014-07-16 14:06:00 +04:00
$msg = 'set shareFileKeyFailed (code: ' . $e -> getCode () . '): ' . $e -> getMessage ();
\OCP\Util :: writeLog ( 'files_encryption' , $msg , \OCP\Util :: FATAL );
return false ;
} catch ( \Exception $e ) {
$msg = 'set shareFileKeyFailed (unknown error): ' . $e -> getMessage ();
\OCP\Util :: writeLog ( 'files_encryption' , $msg , \OCP\Util :: FATAL );
return false ;
}
2013-05-20 03:24:36 +04:00
2013-02-26 22:11:29 +04:00
// Save the recrypted key to it's owner's keyfiles directory
// Save new sharekeys to all necessary user directory
2013-05-20 03:24:36 +04:00
if (
2014-07-16 14:06:00 +04:00
! Keymanager :: setFileKey ( $this -> view , $this , $filePath , $multiEncKey [ 'data' ])
|| ! Keymanager :: setShareKeys ( $this -> view , $this , $filePath , $multiEncKey [ 'keys' ])
2013-02-26 22:11:29 +04:00
) {
2013-02-20 23:18:00 +04:00
2013-05-27 14:41:55 +04:00
\OCP\Util :: writeLog ( 'Encryption library' ,
'Keyfiles could not be saved for users sharing ' . $filePath , \OCP\Util :: ERROR );
2013-05-20 03:24:36 +04:00
2013-05-09 16:43:06 +04:00
return false ;
2013-02-20 23:18:00 +04:00
}
2013-05-20 03:24:36 +04:00
2013-05-09 16:43:06 +04:00
// Return proxy to original status
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-02-20 23:18:00 +04:00
return true ;
}
2013-05-20 03:24:36 +04:00
2013-04-16 20:29:22 +04:00
/**
2014-05-19 19:50:53 +04:00
* Find , sanitise and format users sharing a file
2013-04-16 20:29:22 +04:00
* @ note This wraps other methods into a portable bundle
2014-02-06 19:30:58 +04:00
* @ param boolean $sharingEnabled
2014-03-31 20:09:46 +04:00
* @ param string $filePath path relativ to current users files folder
2013-04-16 20:29:22 +04:00
*/
2014-03-31 20:09:46 +04:00
public function getSharingUsersArray ( $sharingEnabled , $filePath ) {
2012-07-11 20:51:27 +04:00
2014-02-13 19:28:49 +04:00
$appConfig = \OC :: $server -> getAppConfig ();
2013-04-16 20:29:22 +04:00
// Check if key recovery is enabled
2013-05-04 18:14:38 +04:00
if (
2014-02-13 19:28:49 +04:00
$appConfig -> getValue ( 'files_encryption' , 'recoveryAdminEnabled' )
2013-05-04 18:14:38 +04:00
&& $this -> recoveryEnabledForUser ()
) {
$recoveryEnabled = true ;
} else {
$recoveryEnabled = false ;
}
2013-05-20 03:24:36 +04:00
2013-04-16 20:29:22 +04:00
// Make sure that a share key is generated for the owner too
2013-05-27 14:41:55 +04:00
list ( $owner , $ownerPath ) = $this -> getUidAndFilename ( $filePath );
2013-05-20 03:24:36 +04:00
2014-12-03 18:52:44 +03:00
$ownerPath = Helper :: stripPartialFileExtension ( $ownerPath );
2013-08-27 18:29:54 +04:00
2014-03-31 20:09:46 +04:00
// always add owner to the list of users with access to the file
$userIds = array ( $owner );
2013-05-27 14:41:55 +04:00
if ( $sharingEnabled ) {
2013-04-18 19:53:59 +04:00
2013-04-16 20:29:22 +04:00
// Find out who, if anyone, is sharing the file
2014-03-31 20:09:46 +04:00
$result = \OCP\Share :: getUsersSharingFile ( $ownerPath , $owner );
$userIds = \array_merge ( $userIds , $result [ 'users' ]);
2015-01-29 15:09:44 +03:00
if ( $result [ 'public' ] || $result [ 'remote' ]) {
2013-05-13 19:40:57 +04:00
$userIds [] = $this -> publicShareKeyId ;
}
2013-05-20 03:24:36 +04:00
2013-04-16 20:29:22 +04:00
}
2013-05-20 03:24:36 +04:00
2013-08-18 13:21:01 +04:00
// If recovery is enabled, add the
2013-04-16 20:29:22 +04:00
// Admin UID to list of users to share to
2013-05-27 14:41:55 +04:00
if ( $recoveryEnabled ) {
2013-05-04 18:14:38 +04:00
// Find recoveryAdmin user ID
2014-02-13 19:28:49 +04:00
$recoveryKeyId = $appConfig -> getValue ( 'files_encryption' , 'recoveryKeyId' );
2013-05-04 18:14:38 +04:00
// Add recoveryAdmin to list of users sharing
2013-05-13 19:26:21 +04:00
$userIds [] = $recoveryKeyId ;
2013-06-25 14:21:54 +04:00
}
2013-05-20 03:24:36 +04:00
2013-06-25 14:21:54 +04:00
// check if it is a group mount
2013-06-25 19:46:04 +04:00
if ( \OCP\App :: isEnabled ( " files_external " )) {
2014-08-05 17:25:52 +04:00
$mounts = \OC_Mount_Config :: getSystemMountPoints ();
foreach ( $mounts as $mount ) {
if ( $mount [ 'mountpoint' ] == substr ( $ownerPath , 1 , strlen ( $mount [ 'mountpoint' ]))) {
$userIds = array_merge ( $userIds , $this -> getUserWithAccessToMountPoint ( $mount [ 'applicable' ][ 'users' ], $mount [ 'applicable' ][ 'groups' ]));
2013-06-25 19:46:04 +04:00
}
2013-06-25 14:21:54 +04:00
}
2013-05-04 18:14:38 +04:00
}
2013-04-22 14:25:55 +04:00
2013-04-16 20:29:22 +04:00
// Remove duplicate UIDs
2013-05-27 14:41:55 +04:00
$uniqueUserIds = array_unique ( $userIds );
2013-05-20 03:24:36 +04:00
2013-04-16 20:29:22 +04:00
return $uniqueUserIds ;
}
2013-05-20 03:24:36 +04:00
2013-06-25 14:21:54 +04:00
private function getUserWithAccessToMountPoint ( $users , $groups ) {
$result = array ();
if ( in_array ( 'all' , $users )) {
$result = \OCP\User :: getUsers ();
} else {
$result = array_merge ( $result , $users );
foreach ( $groups as $group ) {
$result = array_merge ( $result , \OC_Group :: usersInGroup ( $group ));
}
}
return $result ;
}
2013-05-04 18:14:38 +04:00
/**
2014-05-19 19:50:53 +04:00
* set migration status
2014-02-10 19:43:04 +04:00
* @ param int $status
2014-05-06 21:20:49 +04:00
* @ param int $preCondition only update migration status if the previous value equals $preCondition
2013-06-11 14:03:50 +04:00
* @ return boolean
2013-05-04 18:14:38 +04:00
*/
2014-05-06 21:20:49 +04:00
private function setMigrationStatus ( $status , $preCondition = null ) {
2013-05-20 03:24:36 +04:00
2014-05-06 21:20:49 +04:00
// convert to string if preCondition is set
$preCondition = ( $preCondition === null ) ? null : ( string ) $preCondition ;
2013-05-20 03:24:36 +04:00
2014-12-04 18:48:07 +03:00
try {
\OC :: $server -> getConfig () -> setUserValue ( $this -> userId , 'files_encryption' , 'migration_status' , ( string ) $status , $preCondition );
return true ;
} catch ( \OCP\PreConditionNotMetException $e ) {
return false ;
}
2014-02-10 19:43:04 +04:00
}
/**
2014-05-19 19:50:53 +04:00
* start migration mode to initially encrypt users data
2014-02-10 19:43:04 +04:00
* @ return boolean
*/
public function beginMigration () {
2014-05-06 21:20:49 +04:00
$result = $this -> setMigrationStatus ( self :: MIGRATION_IN_PROGRESS , self :: MIGRATION_OPEN );
2014-02-10 19:43:04 +04:00
if ( $result ) {
2013-06-12 14:21:11 +04:00
\OCP\Util :: writeLog ( 'Encryption library' , " Start migration to encryption mode for " . $this -> userId , \OCP\Util :: INFO );
2013-06-11 14:03:50 +04:00
} else {
2013-06-12 14:21:11 +04:00
\OCP\Util :: writeLog ( 'Encryption library' , " Could not activate migration mode for " . $this -> userId . " . Probably another process already started the initial encryption " , \OCP\Util :: WARN );
2013-06-11 14:03:50 +04:00
}
2013-05-20 03:24:36 +04:00
2014-02-10 19:43:04 +04:00
return $result ;
}
public function resetMigrationStatus () {
return $this -> setMigrationStatus ( self :: MIGRATION_OPEN );
2013-06-11 14:03:50 +04:00
}
2013-05-20 03:24:36 +04:00
2013-06-11 14:03:50 +04:00
/**
2014-05-19 19:50:53 +04:00
* close migration mode after users data has been encrypted successfully
2013-06-11 14:03:50 +04:00
* @ return boolean
*/
public function finishMigration () {
2014-02-10 19:43:04 +04:00
$result = $this -> setMigrationStatus ( self :: MIGRATION_COMPLETED );
2013-05-20 03:24:36 +04:00
2014-02-10 19:43:04 +04:00
if ( $result ) {
2013-06-12 14:21:11 +04:00
\OCP\Util :: writeLog ( 'Encryption library' , " Finish migration successfully for " . $this -> userId , \OCP\Util :: INFO );
2013-06-11 14:03:50 +04:00
} else {
2013-06-12 14:21:11 +04:00
\OCP\Util :: writeLog ( 'Encryption library' , " Could not deactivate migration mode for " . $this -> userId , \OCP\Util :: WARN );
2013-05-04 18:14:38 +04:00
}
2013-05-20 03:24:36 +04:00
2014-02-10 19:43:04 +04:00
return $result ;
2013-05-04 18:14:38 +04:00
}
2013-05-20 03:24:36 +04:00
2013-05-04 18:14:38 +04:00
/**
2014-05-19 19:50:53 +04:00
* check if files are already migrated to the encryption system
2014-05-13 15:29:25 +04:00
* @ return int | false migration status , false = in case of no record
2013-05-20 03:24:36 +04:00
* @ note If records are not being returned , check for a hidden space
2013-05-04 18:14:38 +04:00
* at the start of the uid in db
*/
2013-11-27 18:35:32 +04:00
public function getMigrationStatus () {
2013-05-04 18:14:38 +04:00
2014-05-06 21:20:49 +04:00
$migrationStatus = false ;
if ( \OCP\User :: userExists ( $this -> userId )) {
2014-12-04 18:48:07 +03:00
$migrationStatus = \OC :: $server -> getConfig () -> getUserValue ( $this -> userId , 'files_encryption' , 'migration_status' , null );
2014-05-06 21:20:49 +04:00
if ( $migrationStatus === null ) {
2014-12-04 18:48:07 +03:00
\OC :: $server -> getConfig () -> setUserValue ( $this -> userId , 'files_encryption' , 'migration_status' , ( string ) self :: MIGRATION_OPEN );
2014-05-06 21:20:49 +04:00
$migrationStatus = self :: MIGRATION_OPEN ;
2013-05-26 01:07:19 +04:00
}
2013-05-04 18:14:38 +04:00
}
2013-05-20 03:24:36 +04:00
2014-05-06 21:20:49 +04:00
return ( int ) $migrationStatus ;
2013-05-20 03:24:36 +04:00
2013-05-04 18:14:38 +04:00
}
2013-05-20 03:24:36 +04:00
2013-03-30 00:11:29 +04:00
/**
2014-05-19 19:50:53 +04:00
* get uid of the owners of the file and the path to the file
2013-05-20 03:24:36 +04:00
* @ param string $path Path of the file to check
2013-05-30 03:13:22 +04:00
* @ throws \Exception
2013-05-20 03:24:36 +04:00
* @ note $shareFilePath must be relative to data / UID / files . Files
2013-04-10 19:37:03 +04:00
* relative to / Shared are also acceptable
2013-03-26 15:39:55 +04:00
* @ return array
*/
2013-05-27 14:41:55 +04:00
public function getUidAndFilename ( $path ) {
2013-05-20 03:24:36 +04:00
2013-08-22 19:55:10 +04:00
$pathinfo = pathinfo ( $path );
$partfile = false ;
$parentFolder = false ;
2013-08-27 16:19:30 +04:00
if ( array_key_exists ( 'extension' , $pathinfo ) && $pathinfo [ 'extension' ] === 'part' ) {
2013-08-22 19:55:10 +04:00
// if the real file exists we check this file
2013-09-05 12:11:09 +04:00
$filePath = $this -> userFilesDir . '/' . $pathinfo [ 'dirname' ] . '/' . $pathinfo [ 'filename' ];
if ( $this -> view -> file_exists ( $filePath )) {
2013-08-22 19:55:10 +04:00
$pathToCheck = $pathinfo [ 'dirname' ] . '/' . $pathinfo [ 'filename' ];
} else { // otherwise we look for the parent
$pathToCheck = $pathinfo [ 'dirname' ];
$parentFolder = true ;
}
$partfile = true ;
} else {
$pathToCheck = $path ;
}
2013-05-27 14:41:55 +04:00
$view = new \OC\Files\View ( $this -> userFilesDir );
2013-08-22 19:55:10 +04:00
$fileOwnerUid = $view -> getOwner ( $pathToCheck );
2013-04-18 18:37:49 +04:00
2013-05-20 03:24:36 +04:00
// handle public access
2013-05-27 14:41:55 +04:00
if ( $this -> isPublic ) {
2014-12-04 21:51:04 +03:00
return array ( $this -> userId , $path );
2013-05-20 03:24:36 +04:00
} else {
2013-05-13 23:24:59 +04:00
2013-05-20 03:24:36 +04:00
// Check that UID is valid
2013-05-27 14:41:55 +04:00
if ( ! \OCP\User :: userExists ( $fileOwnerUid )) {
throw new \Exception (
'Could not find owner (UID = "' . var_export ( $fileOwnerUid , 1 ) . '") of file "' . $path . '"' );
2013-05-20 03:24:36 +04:00
}
2013-05-13 23:24:59 +04:00
2013-05-20 03:24:36 +04:00
// NOTE: Bah, this dependency should be elsewhere
2013-05-27 14:41:55 +04:00
\OC\Files\Filesystem :: initMountPoints ( $fileOwnerUid );
2013-05-13 23:24:59 +04:00
2013-05-20 03:24:36 +04:00
// If the file owner is the currently logged in user
2013-05-27 22:44:38 +04:00
if ( $fileOwnerUid === $this -> userId ) {
2013-05-13 23:24:59 +04:00
2013-05-20 03:24:36 +04:00
// Assume the path supplied is correct
$filename = $path ;
2013-05-13 23:24:59 +04:00
2013-05-20 03:24:36 +04:00
} else {
2013-08-22 19:55:10 +04:00
$info = $view -> getFileInfo ( $pathToCheck );
2013-05-27 14:41:55 +04:00
$ownerView = new \OC\Files\View ( '/' . $fileOwnerUid . '/files' );
2013-05-13 23:24:59 +04:00
2013-05-20 03:24:36 +04:00
// Fetch real file path from DB
2013-08-22 19:55:10 +04:00
$filename = $ownerView -> getPath ( $info [ 'fileid' ]);
if ( $parentFolder ) {
$filename = $filename . '/' . $pathinfo [ 'filename' ];
}
if ( $partfile ) {
$filename = $filename . '.' . $pathinfo [ 'extension' ];
}
2013-05-13 23:24:59 +04:00
2013-05-20 03:24:36 +04:00
}
2013-05-13 23:24:59 +04:00
2013-05-27 14:41:55 +04:00
return array (
$fileOwnerUid ,
2014-05-12 18:20:07 +04:00
\OC\Files\Filesystem :: normalizePath ( $filename )
2013-05-27 14:41:55 +04:00
);
2013-05-20 03:24:36 +04:00
}
2013-03-26 15:39:55 +04:00
}
2013-04-22 13:58:39 +04:00
/**
2014-05-19 19:50:53 +04:00
* go recursively through a dir and collect all files and sub files .
2013-05-20 03:24:36 +04:00
* @ param string $dir relative to the users files folder
2013-04-22 13:58:39 +04:00
* @ return array with list of files relative to the users files folder
*/
2014-04-15 22:18:04 +04:00
public function getAllFiles ( $dir , $mountPoint = '' ) {
2013-04-22 13:58:39 +04:00
$result = array ();
2014-01-22 19:55:04 +04:00
$dirList = array ( $dir );
2013-05-01 13:04:40 +04:00
2014-01-22 19:55:04 +04:00
while ( $dirList ) {
$dir = array_pop ( $dirList );
$content = $this -> view -> getDirectoryContent ( \OC\Files\Filesystem :: normalizePath (
$this -> userFilesDir . '/' . $dir ));
2013-05-20 03:24:36 +04:00
2014-01-22 19:55:04 +04:00
foreach ( $content as $c ) {
2014-04-15 22:18:04 +04:00
// getDirectoryContent() returns the paths relative to the mount points, so we need
// to re-construct the complete path
$path = ( $mountPoint !== '' ) ? $mountPoint . '/' . $c [ 'path' ] : $c [ 'path' ];
2014-05-28 19:18:41 +04:00
$path = \OC\Files\Filesystem :: normalizePath ( $path );
2014-01-22 19:55:04 +04:00
if ( $c [ 'type' ] === 'dir' ) {
2014-05-28 19:18:41 +04:00
$dirList [] = substr ( $path , strlen ( '/' . \OCP\User :: getUser () . " /files " ));
2013-05-09 16:43:06 +04:00
} else {
2014-05-28 19:18:41 +04:00
$result [] = substr ( $path , strlen ( '/' . \OCP\User :: getUser () . " /files " ));
2013-05-09 16:43:06 +04:00
}
}
2013-05-06 01:41:42 +04:00
2013-04-22 13:58:39 +04:00
}
2013-05-20 03:24:36 +04:00
2013-04-22 13:58:39 +04:00
return $result ;
}
2013-05-09 20:16:59 +04:00
/**
2014-05-19 19:50:53 +04:00
* get owner of the shared files .
2014-05-13 15:29:25 +04:00
* @ param int $id ID of a share
2013-05-20 03:24:36 +04:00
* @ return string owner
2013-05-09 20:16:59 +04:00
*/
2013-05-27 14:41:55 +04:00
public function getOwnerFromSharedFile ( $id ) {
2013-05-24 01:56:31 +04:00
2013-05-27 14:41:55 +04:00
$query = \OCP\DB :: prepare ( 'SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?' , 1 );
2013-05-25 23:33:05 +04:00
2013-05-27 14:41:55 +04:00
$result = $query -> execute ( array ( $id ));
2013-05-25 23:33:05 +04:00
2013-12-18 21:23:07 +04:00
$source = null ;
2013-05-27 14:41:55 +04:00
if ( \OCP\DB :: isError ( $result )) {
\OCP\Util :: writeLog ( 'Encryption library' , \OC_DB :: getErrorMessage ( $result ), \OCP\Util :: ERROR );
2013-05-26 01:07:19 +04:00
} else {
2013-12-18 21:23:07 +04:00
$source = $result -> fetchRow ();
2013-05-25 23:33:05 +04:00
}
2013-05-24 01:56:31 +04:00
$fileOwner = false ;
2013-05-20 03:24:36 +04:00
2013-12-18 21:23:07 +04:00
if ( $source && isset ( $source [ 'parent' ])) {
2013-05-09 20:16:59 +04:00
$parent = $source [ 'parent' ];
2013-05-20 03:24:36 +04:00
2013-05-27 14:41:55 +04:00
while ( isset ( $parent )) {
2013-05-20 03:24:36 +04:00
2013-05-27 14:41:55 +04:00
$query = \OCP\DB :: prepare ( 'SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?' , 1 );
2013-05-25 23:33:05 +04:00
2013-05-27 14:41:55 +04:00
$result = $query -> execute ( array ( $parent ));
2013-05-25 23:33:05 +04:00
2013-12-18 21:23:07 +04:00
$item = null ;
2013-05-27 14:41:55 +04:00
if ( \OCP\DB :: isError ( $result )) {
\OCP\Util :: writeLog ( 'Encryption library' , \OC_DB :: getErrorMessage ( $result ), \OCP\Util :: ERROR );
2013-05-26 01:07:19 +04:00
} else {
2013-12-18 21:23:07 +04:00
$item = $result -> fetchRow ();
2013-05-25 23:33:05 +04:00
}
2013-12-18 21:23:07 +04:00
if ( $item && isset ( $item [ 'parent' ])) {
2013-05-20 03:24:36 +04:00
2013-05-09 20:16:59 +04:00
$parent = $item [ 'parent' ];
2013-05-20 03:24:36 +04:00
2013-05-09 20:16:59 +04:00
} else {
2013-05-20 03:24:36 +04:00
2013-05-09 20:16:59 +04:00
$fileOwner = $item [ 'uid_owner' ];
2013-05-20 03:24:36 +04:00
2013-05-09 20:16:59 +04:00
break ;
2013-05-20 03:24:36 +04:00
2013-05-09 20:16:59 +04:00
}
}
2013-05-20 03:24:36 +04:00
2013-05-09 20:16:59 +04:00
} else {
2013-05-20 03:24:36 +04:00
2013-05-09 20:16:59 +04:00
$fileOwner = $source [ 'uid_owner' ];
2013-05-20 03:24:36 +04:00
2013-05-09 16:43:06 +04:00
}
return $fileOwner ;
2013-05-20 03:24:36 +04:00
2013-05-09 16:43:06 +04:00
}
2013-05-06 01:41:42 +04:00
2013-05-20 03:24:36 +04:00
/**
* @ return string
*/
2013-05-24 01:56:31 +04:00
public function getUserId () {
2013-05-20 03:24:36 +04:00
return $this -> userId ;
}
2013-05-13 23:24:59 +04:00
2013-11-21 13:09:07 +04:00
/**
* @ return string
*/
public function getKeyId () {
return $this -> keyId ;
}
2013-05-20 03:24:36 +04:00
/**
* @ return string
*/
2013-05-24 01:56:31 +04:00
public function getUserFilesDir () {
2013-05-20 03:24:36 +04:00
return $this -> userFilesDir ;
}
2013-05-13 23:24:59 +04:00
2013-05-20 03:24:36 +04:00
/**
2014-05-13 15:29:25 +04:00
* @ param string $password
2013-05-20 03:24:36 +04:00
* @ return bool
*/
2013-05-27 14:41:55 +04:00
public function checkRecoveryPassword ( $password ) {
2013-05-15 18:12:20 +04:00
2013-06-03 16:19:31 +04:00
$result = false ;
2013-05-15 18:12:20 +04:00
2014-11-14 19:30:38 +03:00
$recoveryKey = Keymanager :: getPrivateSystemKey ( $this -> recoveryKeyId );
2013-06-03 16:19:31 +04:00
$decryptedRecoveryKey = Crypt :: decryptPrivateKey ( $recoveryKey , $password );
2013-05-15 18:12:20 +04:00
2013-06-03 16:19:31 +04:00
if ( $decryptedRecoveryKey ) {
$result = true ;
}
2013-05-15 18:12:20 +04:00
2013-06-03 16:19:31 +04:00
return $result ;
2013-05-15 18:12:20 +04:00
}
2013-05-20 03:24:36 +04:00
/**
* @ return string
*/
2013-05-24 01:56:31 +04:00
public function getRecoveryKeyId () {
2013-05-15 18:12:20 +04:00
return $this -> recoveryKeyId ;
}
2013-05-15 19:56:45 +04:00
/**
2014-05-19 19:50:53 +04:00
* add recovery key to all encrypted files
2013-05-15 19:56:45 +04:00
*/
2013-05-27 14:41:55 +04:00
public function addRecoveryKeys ( $path = '/' ) {
2014-11-10 14:40:24 +03:00
$dirContent = $this -> view -> getDirectoryContent ( $this -> keysPath . '/' . $path );
2013-05-27 14:41:55 +04:00
foreach ( $dirContent as $item ) {
2013-05-24 01:56:31 +04:00
// get relative path from files_encryption/keyfiles/
2014-11-10 14:40:24 +03:00
$filePath = substr ( $item [ 'path' ], strlen ( 'files_encryption/keys' ));
if ( $this -> view -> is_dir ( $this -> userFilesDir . '/' . $filePath )) {
2013-05-27 14:41:55 +04:00
$this -> addRecoveryKeys ( $filePath . '/' );
2013-05-15 19:56:45 +04:00
} else {
2014-12-03 18:52:44 +03:00
$session = new Session ( new \OC\Files\View ( '/' ));
2013-05-15 19:56:45 +04:00
$sharingEnabled = \OCP\Share :: isEnabled ();
2014-11-10 14:40:24 +03:00
$usersSharing = $this -> getSharingUsersArray ( $sharingEnabled , $filePath );
$this -> setSharedFileKeyfiles ( $session , $usersSharing , $filePath );
2013-05-15 19:56:45 +04:00
}
}
}
2013-05-16 16:53:04 +04:00
/**
2014-05-19 19:50:53 +04:00
* remove recovery key to all encrypted files
2013-05-15 19:56:45 +04:00
*/
2013-05-27 14:41:55 +04:00
public function removeRecoveryKeys ( $path = '/' ) {
2014-11-10 14:40:24 +03:00
$dirContent = $this -> view -> getDirectoryContent ( $this -> keysPath . '/' . $path );
2013-05-27 14:41:55 +04:00
foreach ( $dirContent as $item ) {
2013-05-24 01:56:31 +04:00
// get relative path from files_encryption/keyfiles
2014-11-10 14:40:24 +03:00
$filePath = substr ( $item [ 'path' ], strlen ( 'files_encryption/keys' ));
if ( $this -> view -> is_dir ( $this -> userFilesDir . '/' . $filePath )) {
2013-05-27 14:41:55 +04:00
$this -> removeRecoveryKeys ( $filePath . '/' );
2013-05-15 19:56:45 +04:00
} else {
2014-11-10 14:40:24 +03:00
$this -> view -> unlink ( $this -> keysPath . '/' . $filePath . '/' . $this -> recoveryKeyId . '.shareKey' );
2013-05-15 19:56:45 +04:00
}
}
}
2013-05-16 16:53:04 +04:00
/**
2014-05-19 19:50:53 +04:00
* decrypt given file with recovery key and encrypt it again to the owner and his new key
2013-05-20 03:24:36 +04:00
* @ param string $file
* @ param string $privateKey recovery key to decrypt the file
2013-05-16 16:53:04 +04:00
*/
2013-05-27 14:41:55 +04:00
private function recoverFile ( $file , $privateKey ) {
2013-05-16 16:53:04 +04:00
$sharingEnabled = \OCP\Share :: isEnabled ();
// Find out who, if anyone, is sharing the file
2013-05-27 14:41:55 +04:00
if ( $sharingEnabled ) {
2013-05-29 15:10:26 +04:00
$result = \OCP\Share :: getUsersSharingFile ( $file , $this -> userId , true );
2013-05-16 16:53:04 +04:00
$userIds = $result [ 'users' ];
$userIds [] = $this -> recoveryKeyId ;
2013-05-27 14:41:55 +04:00
if ( $result [ 'public' ]) {
2013-05-16 16:53:04 +04:00
$userIds [] = $this -> publicShareKeyId ;
}
} else {
2013-05-27 14:41:55 +04:00
$userIds = array (
$this -> userId ,
$this -> recoveryKeyId
);
2013-05-16 16:53:04 +04:00
}
2013-05-27 14:41:55 +04:00
$filteredUids = $this -> filterShareReadyUsers ( $userIds );
2013-05-16 16:53:04 +04:00
//decrypt file key
2014-11-10 14:40:24 +03:00
$encKeyfile = Keymanager :: getFileKey ( $this -> view , $this , $file );
$shareKey = Keymanager :: getShareKey ( $this -> view , $this -> recoveryKeyId , $this , $file );
2013-05-27 14:41:55 +04:00
$plainKeyfile = Crypt :: multiKeyDecrypt ( $encKeyfile , $shareKey , $privateKey );
2013-05-16 16:53:04 +04:00
// encrypt file key again to all users, this time with the new public key for the recovered use
2013-05-27 14:41:55 +04:00
$userPubKeys = Keymanager :: getPublicKeys ( $this -> view , $filteredUids [ 'ready' ]);
$multiEncKey = Crypt :: multiKeyEncrypt ( $plainKeyfile , $userPubKeys );
2013-05-16 16:53:04 +04:00
2014-11-10 14:40:24 +03:00
Keymanager :: setFileKey ( $this -> view , $this , $file , $multiEncKey [ 'data' ]);
Keymanager :: setShareKeys ( $this -> view , $this , $file , $multiEncKey [ 'keys' ]);
2013-05-16 16:53:04 +04:00
}
/**
2014-05-19 19:50:53 +04:00
* collect all files and recover them one by one
2013-05-20 03:24:36 +04: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 16:53:04 +04:00
*/
2013-05-27 14:41:55 +04:00
private function recoverAllFiles ( $path , $privateKey ) {
2014-11-10 14:40:24 +03:00
$dirContent = $this -> view -> getDirectoryContent ( $this -> keysPath . '/' . $path );
2013-05-27 14:41:55 +04:00
foreach ( $dirContent as $item ) {
2013-05-29 14:23:33 +04:00
// get relative path from files_encryption/keyfiles
2014-11-10 14:40:24 +03:00
$filePath = substr ( $item [ 'path' ], strlen ( 'files_encryption/keys' ));
if ( $this -> view -> is_dir ( $this -> userFilesDir . '/' . $filePath )) {
2013-05-27 14:41:55 +04:00
$this -> recoverAllFiles ( $filePath . '/' , $privateKey );
2013-05-16 16:53:04 +04:00
} else {
2014-11-10 14:40:24 +03:00
$this -> recoverFile ( $filePath , $privateKey );
2013-05-16 16:53:04 +04:00
}
}
}
/**
2014-05-19 19:50:53 +04:00
* recover users files in case of password lost
2013-05-20 03:24:36 +04:00
* @ param string $recoveryPassword
2013-05-16 16:53:04 +04:00
*/
2013-05-27 14:41:55 +04:00
public function recoverUsersFiles ( $recoveryPassword ) {
2013-05-16 16:53:04 +04:00
2014-11-14 19:30:38 +03:00
$encryptedKey = Keymanager :: getPrivateSystemKey ( $this -> recoveryKeyId );
2013-06-03 16:19:31 +04:00
$privateKey = Crypt :: decryptPrivateKey ( $encryptedKey , $recoveryPassword );
2013-05-16 16:53:04 +04:00
2013-05-27 14:41:55 +04:00
$this -> recoverAllFiles ( '/' , $privateKey );
2013-05-16 16:53:04 +04:00
}
2013-05-23 22:30:07 +04:00
2014-09-16 17:16:27 +04:00
/**
* create a backup of all keys from the user
*
2015-01-13 14:45:33 +03:00
* @ param string $purpose define the purpose of the backup , will be part of the backup folder name
* @ param boolean $timestamp ( optional ) should a timestamp be added , default true
* @ param boolean $includeUserKeys ( optional ) include users private -/ public - key , default true
2014-09-16 17:16:27 +04:00
*/
2015-01-13 14:45:33 +03:00
public function backupAllKeys ( $purpose , $timestamp = true , $includeUserKeys = true ) {
2014-09-16 17:16:27 +04:00
$this -> userId ;
2015-01-13 14:45:33 +03:00
$backupDir = $this -> encryptionDir . '/backup.' . $purpose ;
$backupDir .= ( $timestamp ) ? '.' . date ( " Y-m-d_H-i-s " ) . '/' : '/' ;
2014-09-16 17:16:27 +04:00
$this -> view -> mkdir ( $backupDir );
2014-11-10 14:40:24 +03:00
$this -> view -> copy ( $this -> keysPath , $backupDir . 'keys/' );
2015-01-13 14:45:33 +03:00
if ( $includeUserKeys ) {
$this -> view -> copy ( $this -> privateKeyPath , $backupDir . $this -> userId . '.privateKey' );
$this -> view -> copy ( $this -> publicKeyPath , $backupDir . $this -> userId . '.publicKey' );
}
}
/**
* restore backup
*
* @ param string $backup complete name of the backup
* @ return boolean
*/
public function restoreBackup ( $backup ) {
$backupDir = $this -> encryptionDir . '/backup.' . $backup . '/' ;
$fileKeysRestored = $this -> view -> rename ( $backupDir . 'keys' , $this -> encryptionDir . '/keys' );
$pubKeyRestored = $privKeyRestored = true ;
if (
$this -> view -> file_exists ( $backupDir . $this -> userId . '.privateKey' ) &&
$this -> view -> file_exists ( $backupDir . $this -> userId . '.privateKey' )
) {
$pubKeyRestored = $this -> view -> rename ( $backupDir . $this -> userId . '.publicKey' , $this -> publicKeyPath );
$privKeyRestored = $this -> view -> rename ( $backupDir . $this -> userId . '.privateKey' , $this -> privateKeyPath );
}
if ( $fileKeysRestored && $pubKeyRestored && $privKeyRestored ) {
$this -> view -> deleteAll ( $backupDir );
return true ;
}
return false ;
}
/**
* delete backup
*
* @ param string $backup complete name of the backup
* @ return boolean
*/
public function deleteBackup ( $backup ) {
$backupDir = $this -> encryptionDir . '/backup.' . $backup . '/' ;
return $this -> view -> deleteAll ( $backupDir );
2014-09-16 17:16:27 +04:00
}
2013-06-25 16:25:00 +04:00
/**
2014-05-19 19:50:53 +04:00
* check if the file is stored on a system wide mount point
2014-05-13 15:29:25 +04:00
* @ param string $path relative to / data / user with leading '/'
2013-06-25 16:25:00 +04:00
* @ return boolean
*/
public function isSystemWideMountPoint ( $path ) {
2014-06-27 14:20:29 +04:00
$normalizedPath = ltrim ( $path , '/' );
2013-06-25 19:46:04 +04:00
if ( \OCP\App :: isEnabled ( " files_external " )) {
2014-08-05 17:25:52 +04:00
$mounts = \OC_Mount_Config :: getSystemMountPoints ();
foreach ( $mounts as $mount ) {
if ( $mount [ 'mountpoint' ] == substr ( $normalizedPath , 0 , strlen ( $mount [ 'mountpoint' ]))) {
if ( $this -> isMountPointApplicableToUser ( $mount )) {
return true ;
}
2013-06-25 19:46:04 +04:00
}
2013-06-25 16:25:00 +04:00
}
}
return false ;
}
2014-08-05 17:25:52 +04:00
/**
* check if mount point is applicable to user
*
* @ param array $mount contains $mount [ 'applicable' ][ 'users' ], $mount [ 'applicable' ][ 'groups' ]
* @ return boolean
*/
protected function isMountPointApplicableToUser ( $mount ) {
$uid = \OCP\User :: getUser ();
$acceptedUids = array ( 'all' , $uid );
// check if mount point is applicable for the user
$intersection = array_intersect ( $acceptedUids , $mount [ 'applicable' ][ 'users' ]);
if ( ! empty ( $intersection )) {
return true ;
}
// check if mount point is applicable for group where the user is a member
foreach ( $mount [ 'applicable' ][ 'groups' ] as $gid ) {
if ( \OC_Group :: inGroup ( $uid , $gid )) {
return true ;
}
}
return false ;
}
2013-07-29 19:06:05 +04:00
/**
2014-05-19 19:50:53 +04:00
* decrypt private key and add it to the current session
2013-07-29 19:06:05 +04:00
* @ param array $params with 'uid' and 'password'
* @ return mixed session or false
*/
public function initEncryption ( $params ) {
2014-12-03 18:52:44 +03:00
$session = new Session ( $this -> view );
2013-09-02 13:26:11 +04:00
// we tried to initialize the encryption app for this session
2014-12-03 18:52:44 +03:00
$session -> setInitialized ( Session :: INIT_EXECUTED );
2013-09-02 13:26:11 +04:00
2013-07-29 19:06:05 +04:00
$encryptedKey = Keymanager :: getPrivateKey ( $this -> view , $params [ 'uid' ]);
2014-11-14 19:30:38 +03:00
$privateKey = false ;
if ( $encryptedKey ) {
$privateKey = Crypt :: decryptPrivateKey ( $encryptedKey , $params [ 'password' ]);
}
2013-07-29 19:06:05 +04:00
if ( $privateKey === false ) {
\OCP\Util :: writeLog ( 'Encryption library' , 'Private key for user "' . $params [ 'uid' ]
. '" is not valid! Maybe the user password was changed from outside if so please change it back to gain access' , \OCP\Util :: ERROR );
return false ;
}
$session -> setPrivateKey ( $privateKey );
2014-12-03 18:52:44 +03:00
$session -> setInitialized ( Session :: INIT_SUCCESSFUL );
2013-08-18 13:21:01 +04:00
2013-07-29 19:06:05 +04:00
return $session ;
}
2013-08-18 13:21:01 +04:00
2014-02-26 20:18:38 +04:00
/*
2014-05-19 19:50:53 +04:00
* remove encryption related keys from the session
2014-02-26 20:18:38 +04:00
*/
public function closeEncryptionSession () {
2014-12-03 18:52:44 +03:00
$session = new Session ( $this -> view );
2014-02-26 20:18:38 +04:00
$session -> closeSession ();
}
2012-07-11 20:51:27 +04:00
}