initial integration of encryption
This commit is contained in:
parent
e53e7990c4
commit
f1cbb9effc
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<info>
|
||||||
|
<id>files_encryption</id>
|
||||||
|
<name>Encryption</name>
|
||||||
|
<description>Server side encryption of files</description>
|
||||||
|
<version>0.1</version>
|
||||||
|
<licence>AGPL</licence>
|
||||||
|
<author>Robin Appelman</author>
|
||||||
|
<require>3</require>
|
||||||
|
<default_enable/>
|
||||||
|
</info>
|
|
@ -37,20 +37,42 @@ require_once('Crypt_Blowfish/Blowfish.php');
|
||||||
* This class is for crypting and decrypting
|
* This class is for crypting and decrypting
|
||||||
*/
|
*/
|
||||||
class OC_Crypt {
|
class OC_Crypt {
|
||||||
|
static private $bf = null;
|
||||||
|
|
||||||
static $encription_extension='.encrypted';
|
public static function loginListener($params){
|
||||||
|
self::init($params['uid'],$params['password']);
|
||||||
|
}
|
||||||
|
|
||||||
public static function init($login,$password) {
|
public static function init($login,$password) {
|
||||||
$_SESSION['user_password'] = $password; // save the password as passcode for the encryption
|
|
||||||
if(OC_User::isLoggedIn()){
|
if(OC_User::isLoggedIn()){
|
||||||
// does key exist?
|
$view=new OC_FilesystemView('/'.$login);
|
||||||
if(!file_exists(OC_Config::getValue( "datadirectory").'/'.$login.'/encryption.key')){
|
if(!$view->file_exists('/encryption.key')){// does key exist?
|
||||||
OC_Crypt::createkey($_SESSION['user_password']);
|
OC_Crypt::createkey($password);
|
||||||
}
|
}
|
||||||
|
$key=$view->file_get_contents('/encryption.key');
|
||||||
|
$_SESSION['enckey']=OC_Crypt::decrypt($key, $password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the blowfish encryption handeler for a key
|
||||||
|
* @param string $key (optional)
|
||||||
|
*
|
||||||
|
* if the key is left out, the default handeler will be used
|
||||||
|
*/
|
||||||
|
public static function getBlowfish($key=''){
|
||||||
|
if($key){
|
||||||
|
return new Crypt_Blowfish($key);
|
||||||
|
}else{
|
||||||
|
if(!isset($_SESSION['enckey'])){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(!self::$bf){
|
||||||
|
self::$bf=new Crypt_Blowfish($_SESSION['enckey']);
|
||||||
|
}
|
||||||
|
return self::$bf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function createkey($passcode) {
|
public static function createkey($passcode) {
|
||||||
if(OC_User::isLoggedIn()){
|
if(OC_User::isLoggedIn()){
|
||||||
|
@ -61,57 +83,58 @@ class OC_Crypt {
|
||||||
$enckey=OC_Crypt::encrypt($key,$passcode);
|
$enckey=OC_Crypt::encrypt($key,$passcode);
|
||||||
|
|
||||||
// Write the file
|
// Write the file
|
||||||
$username=OC_USER::getUser();
|
$username=OC_USER::getUser();
|
||||||
@file_put_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key', $enckey );
|
OC_FileProxy::$enabled=false;
|
||||||
|
$view=new OC_FilesystemView('/'.$username);
|
||||||
|
$view->file_put_contents('/encryption.key',$enckey);
|
||||||
|
OC_FileProxy::$enabled=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function changekeypasscode( $newpasscode) {
|
public static function changekeypasscode($oldPassword, $newPassword) {
|
||||||
if(OC_User::isLoggedIn()){
|
if(OC_User::isLoggedIn()){
|
||||||
$username=OC_USER::getUser();
|
$username=OC_USER::getUser();
|
||||||
|
$view=new OC_FilesystemView('/'.$username);
|
||||||
|
|
||||||
// read old key
|
// read old key
|
||||||
$key=file_get_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key');
|
$key=$view->file_get_contents('/encryption.key');
|
||||||
|
|
||||||
// decrypt key with old passcode
|
// decrypt key with old passcode
|
||||||
$key=OC_Crypt::decrypt($key, $_SESSION['user_password']);
|
$key=OC_Crypt::decrypt($key, $oldPassword);
|
||||||
|
|
||||||
// encrypt again with new passcode
|
// encrypt again with new passcode
|
||||||
$key=OC_Crypt::encrypt($key,$newpassword);
|
$key=OC_Crypt::encrypt($key, $newPassword);
|
||||||
|
|
||||||
// store the new key
|
// store the new key
|
||||||
file_put_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key', $key );
|
$view->file_put_contents('/encryption.key', $key );
|
||||||
|
|
||||||
$_SESSION['user_password']=$newpasscode;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief encrypts an content
|
* @brief encrypts an content
|
||||||
* @param $content the cleartext message you want to encrypt
|
* @param $content the cleartext message you want to encrypt
|
||||||
* @param $key the encryption key
|
* @param $key the encryption key (optional)
|
||||||
* @returns encrypted content
|
* @returns encrypted content
|
||||||
*
|
*
|
||||||
* This function encrypts an content
|
* This function encrypts an content
|
||||||
*/
|
*/
|
||||||
public static function encrypt( $content, $key) {
|
public static function encrypt( $content, $key='') {
|
||||||
$bf = new Crypt_Blowfish($key);
|
$bf = self::getBlowfish($key);
|
||||||
return($bf->encrypt($content));
|
return($bf->encrypt($content));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
/**
|
* @brief decryption of an content
|
||||||
* @brief decryption of an content
|
* @param $content the cleartext message you want to decrypt
|
||||||
* @param $content the cleartext message you want to decrypt
|
* @param $key the encryption key (optional)
|
||||||
* @param $key the encryption key
|
* @returns cleartext content
|
||||||
* @returns cleartext content
|
*
|
||||||
*
|
* This function decrypts an content
|
||||||
* This function decrypts an content
|
*/
|
||||||
*/
|
public static function decrypt( $content, $key='') {
|
||||||
public static function decrypt( $content, $key) {
|
$bf = self::getBlowfish($key);
|
||||||
$bf = new Crypt_Blowfish($key);
|
|
||||||
return($bf->encrypt($contents));
|
return($bf->encrypt($contents));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief encryption of a file
|
* @brief encryption of a file
|
||||||
|
@ -181,8 +204,4 @@ class OC_Crypt {
|
||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -22,19 +22,35 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* transparently encrypted filestream
|
* transparently encrypted filestream
|
||||||
|
*
|
||||||
|
* you can use it as wrapper around an existing stream by setting OC_CryptStream::$sourceStreams['foo']=array('path'=>$path,'stream'=>$stream)
|
||||||
|
* and then fopen('crypt://streams/foo');
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class OC_CryptStream{
|
class OC_CryptStream{
|
||||||
|
public static $sourceStreams=array();
|
||||||
private $source;
|
private $source;
|
||||||
|
private $path;
|
||||||
|
private $readBuffer;//for streams that dont support seeking
|
||||||
|
private $meta=array();//header/meta for source stream
|
||||||
|
|
||||||
public function stream_open($path, $mode, $options, &$opened_path){
|
public function stream_open($path, $mode, $options, &$opened_path){
|
||||||
$path=str_replace('crypt://','',$path);
|
$path=str_replace('crypt://','',$path);
|
||||||
OC_Log::write('files_encryption','open encrypted '.$path. ' in '.$mode,OC_Log::DEBUG);
|
if(dirname($path)=='streams' and isset(self::$sourceStreams[basename($path)])){
|
||||||
OC_FileProxy::$enabled=false;//disable fileproxies so we can open the source file
|
$this->source=self::$sourceStreams[basename($path)]['stream'];
|
||||||
$this->source=OC_FileSystem::fopen($path,$mode);
|
$this->path=self::$sourceStreams[basename($path)]['path'];
|
||||||
OC_FileProxy::$enabled=true;
|
}else{
|
||||||
if(!is_resource($this->source)){
|
$this->path=$path;
|
||||||
OC_Log::write('files_encryption','failed to open '.$path,OC_Log::ERROR);
|
OC_Log::write('files_encryption','open encrypted '.$path. ' in '.$mode,OC_Log::DEBUG);
|
||||||
|
OC_FileProxy::$enabled=false;//disable fileproxies so we can open the source file
|
||||||
|
$this->source=OC_FileSystem::fopen($path,$mode);
|
||||||
|
OC_FileProxy::$enabled=true;
|
||||||
|
if(!is_resource($this->source)){
|
||||||
|
OC_Log::write('files_encryption','failed to open '.$path,OC_Log::ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(is_resource($this->source)){
|
||||||
|
$this->meta=stream_get_meta_data($this->source);
|
||||||
}
|
}
|
||||||
return is_resource($this->source);
|
return is_resource($this->source);
|
||||||
}
|
}
|
||||||
|
@ -51,14 +67,26 @@ class OC_CryptStream{
|
||||||
$pos=0;
|
$pos=0;
|
||||||
$currentPos=ftell($this->source);
|
$currentPos=ftell($this->source);
|
||||||
$offset=$currentPos%8192;
|
$offset=$currentPos%8192;
|
||||||
fseek($this->source,-$offset,SEEK_CUR);
|
|
||||||
$result='';
|
$result='';
|
||||||
|
if($offset>0){
|
||||||
|
if($this->meta['seekable']){
|
||||||
|
fseek($this->source,-$offset,SEEK_CUR);//if seeking isnt supported the internal read buffer will be used
|
||||||
|
}else{
|
||||||
|
$pos=strlen($this->readBuffer);
|
||||||
|
$result=$this->readBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
while($count>$pos){
|
while($count>$pos){
|
||||||
$data=fread($this->source,8192);
|
$data=fread($this->source,8192);
|
||||||
$pos+=8192;
|
$pos+=8192;
|
||||||
$result.=OC_Crypt::decrypt($data);
|
if(strlen($data)){
|
||||||
|
$result.=OC_Crypt::decrypt($data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return substr($result,$offset,$count);
|
if(!$this->meta['seekable']){
|
||||||
|
$this->readBuffer=substr($result,$count);
|
||||||
|
}
|
||||||
|
return substr($result,0,$count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stream_write($data){
|
public function stream_write($data){
|
||||||
|
@ -119,6 +147,9 @@ class OC_CryptStream{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stream_close(){
|
public function stream_close(){
|
||||||
|
if(OC_FileCache::inCache($this->path)){
|
||||||
|
OC_FileCache::put($this->path,array('encrypted'=>true));
|
||||||
|
}
|
||||||
return fclose($this->source);
|
return fclose($this->source);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -26,38 +26,98 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class OC_FileProxy_Encryption extends OC_FileProxy{
|
class OC_FileProxy_Encryption extends OC_FileProxy{
|
||||||
|
private static $blackList=null; //mimetypes blacklisted from encryption
|
||||||
|
private static $metaData=array(); //metadata cache
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if a file should be encrypted during write
|
||||||
|
* @param string $path
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private static function shouldEncrypt($path){
|
||||||
|
if(is_null(self::$blackList)){
|
||||||
|
self::$blackList=explode(',',OC_Appconfig::getValue('files_encryption','type_blacklist','jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg'));
|
||||||
|
}
|
||||||
|
if(isset(self::$metaData[$path])){
|
||||||
|
$metadata=self::$metaData[$path];
|
||||||
|
}else{
|
||||||
|
$metadata=OC_FileCache::get($path);
|
||||||
|
self::$metaData[$path]=$metadata;
|
||||||
|
}
|
||||||
|
if($metadata['encrypted']){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$extention=substr($path,strrpos($path,'.')+1);
|
||||||
|
if(array_search($extention,self::$blackList)===false){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if a file is encrypted
|
||||||
|
* @param string $path
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private static function isEncrypted($path){
|
||||||
|
if(isset(self::$metaData[$path])){
|
||||||
|
$metadata=self::$metaData[$path];
|
||||||
|
}else{
|
||||||
|
$metadata=OC_FileCache::get($path);
|
||||||
|
self::$metaData[$path]=$metadata;
|
||||||
|
}
|
||||||
|
return (bool)$metadata['encrypted'];
|
||||||
|
}
|
||||||
|
|
||||||
public function preFile_put_contents($path,&$data){
|
public function preFile_put_contents($path,&$data){
|
||||||
if(substr($path,-4)=='.enc'){
|
if(self::shouldEncrypt($path)){
|
||||||
|
$exists=OC_Filesystem::file_exists($path);
|
||||||
|
$target=fopen('crypt://'.$path,'w');
|
||||||
if (is_resource($data)) {
|
if (is_resource($data)) {
|
||||||
$newData='';
|
|
||||||
while(!feof($data)){
|
while(!feof($data)){
|
||||||
$block=fread($data,8192);
|
fwrite($target,fread($data,8192));
|
||||||
$newData.=OC_Crypt::encrypt($block);
|
|
||||||
}
|
}
|
||||||
$data=$newData;
|
|
||||||
}else{
|
}else{
|
||||||
$data=OC_Crypt::blockEncrypt($data);
|
fwrite($target,$data);
|
||||||
}
|
}
|
||||||
|
//fake the normal hooks
|
||||||
|
OC_Hook::emit( 'OC_Filesystem', 'post_create', array( 'path' => $path));
|
||||||
|
OC_Hook::emit( 'OC_Filesystem', 'post_write', array( 'path' => $path));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postFile_get_contents($path,$data){
|
public function postFile_get_contents($path,$data){
|
||||||
if(substr($path,-4)=='.enc'){
|
if(self::isEncrypted($path)){
|
||||||
return OC_Crypt::blockDecrypt($data);
|
$data=OC_Crypt::blockDecrypt($data);
|
||||||
}
|
}
|
||||||
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postFopen($path,&$result){
|
public function postFopen($path,&$result){
|
||||||
if(substr($path,-4)=='.enc'){
|
if(!$result){
|
||||||
$meta=stream_get_meta_data($result);
|
return $result;
|
||||||
|
}
|
||||||
|
$meta=stream_get_meta_data($result);
|
||||||
|
if(self::isEncrypted($path)){
|
||||||
fclose($result);
|
fclose($result);
|
||||||
OC_log::write('file_encryption','mode: '.$meta['mode']);
|
$result=fopen('crypt://'.$path,$meta['mode']);
|
||||||
|
}elseif(self::shouldEncrypt($path) and $meta['mode']!='r'){
|
||||||
|
if(OC_Filesystem::file_exists($path)){
|
||||||
|
//first encrypt the target file so we don't end up with a half encrypted file
|
||||||
|
OC_Log::write('files_encryption','Decrypting '.$path.' before writing',OC_Log::DEBUG);
|
||||||
|
if($result){
|
||||||
|
fclose($result);
|
||||||
|
}
|
||||||
|
$tmpFile=OC_Filesystem::toTmpFile($path);
|
||||||
|
OC_Filesystem::fromTmpFile($tmpFile,$path);
|
||||||
|
}
|
||||||
$result=fopen('crypt://'.$path,$meta['mode']);
|
$result=fopen('crypt://'.$path,$meta['mode']);
|
||||||
}
|
}
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function preReadFile($path){
|
public function preReadFile($path){
|
||||||
if(substr($path,-4)=='.enc'){
|
if(self::isEncrypted($path)){
|
||||||
$stream=fopen('crypt://'.$path,'r');
|
$stream=fopen('crypt://'.$path,'r');
|
||||||
while(!feof($stream)){
|
while(!feof($stream)){
|
||||||
print(fread($stream,8192));
|
print(fread($stream,8192));
|
||||||
|
@ -65,11 +125,4 @@ class OC_FileProxy_Encryption extends OC_FileProxy{
|
||||||
return false;//cancel the original request
|
return false;//cancel the original request
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postGetMimeType($path,$result){
|
|
||||||
if(substr($path,-4)=='.enc'){
|
|
||||||
return 'text/plain';
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,8 +195,8 @@ class OC_User {
|
||||||
if( $run ){
|
if( $run ){
|
||||||
$uid=self::checkPassword( $uid, $password );
|
$uid=self::checkPassword( $uid, $password );
|
||||||
if($uid){
|
if($uid){
|
||||||
OC_Crypt::init($uid,$password);
|
|
||||||
return self::setUserId($uid);
|
return self::setUserId($uid);
|
||||||
|
OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid, 'password'=>$password ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -209,7 +209,6 @@ class OC_User {
|
||||||
*/
|
*/
|
||||||
public static function setUserId($uid) {
|
public static function setUserId($uid) {
|
||||||
$_SESSION['user_id'] = $uid;
|
$_SESSION['user_id'] = $uid;
|
||||||
OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid ));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue