From 0a5462c3311b4541a858b8e719ae8548877c7a62 Mon Sep 17 00:00:00 2001 From: Jerome Jutteau Date: Wed, 24 Jul 2013 14:46:39 +0200 Subject: [PATCH] Add encryption feature (disabled by default). Notes: - This feature requieres mcrypt module to be installed. - File dedupliation will stop working (as files are encrypted). - Server's ressource usage is heavier (encryption/decryption cost). --- file.php | 111 ++++++++++++++++++++++++++++++++++---------- lib/config.php | 5 ++ lib/functions.js | 40 ++++++++++------ lib/functions.php | 114 +++++++++++++++++++++++++++++++++++++++++++--- lib/lang.php | 2 +- script.php | 10 ++-- 6 files changed, 233 insertions(+), 49 deletions(-) diff --git a/file.php b/file.php index 710ce3d..d632beb 100644 --- a/file.php +++ b/file.php @@ -33,7 +33,7 @@ if (!isset ($_GET['h']) || empty ($_GET['h'])) /* Operations may take a long time. * Be sure PHP's safe mode is off. */ - set_time_limit(0); +set_time_limit(0); $link_name = $_GET['h']; @@ -59,6 +59,10 @@ $delete_code = ''; if (isset ($_GET['d']) && !empty ($_GET['d'])) $delete_code = $_GET['d']; +$crypt_key = ''; +if (isset ($_GET['k']) && !empty ($_GET['k'])) + $crypt_key = $_GET['k']; + $button_download = false; if (isset ($_GET['bd']) && !empty ($_GET['bd'])) $button_download = true; @@ -100,6 +104,15 @@ if ($link['time'] != JIRAFEAU_INFINITY && time () > $link['time']) exit; } +if (empty ($crypt_key) && $link['crypted']) +{ + require (JIRAFEAU_ROOT.'lib/template/header.php'); + echo '

' . t('Sorry, the requested file is not found') . + '

'; + require (JIRAFEAU_ROOT.'lib/template/footer.php'); + exit; +} + $password_challenged = false; if (!empty ($link['key'])) { @@ -107,8 +120,10 @@ if (!empty ($link['key'])) { require (JIRAFEAU_ROOT.'lib/template/header.php'); echo '
' . - '
'; ?> + ''; ?> ' . '' . t('Password protection') . @@ -121,14 +136,23 @@ if (!empty ($link['key'])) '' . ''; ?>'; + document.getElementById('submit_download').submit ();"/>'; + document.getElementById('submit_preview').submit ();"/>
'; require (JIRAFEAU_ROOT.'lib/template/footer.php'); @@ -136,7 +160,9 @@ if (!empty ($link['key'])) } else { - if ($link['key'] != md5 ($_POST['key'])) + if ($link['key'] == md5 ($_POST['key'])) + $password_challenged = true; + else { header ("Access denied"); require (JIRAFEAU_ROOT.'lib/template/header.php'); @@ -145,8 +171,6 @@ if (!empty ($link['key'])) require (JIRAFEAU_ROOT.'lib/template/footer.php'); exit; } - else - $password_challenged = true; } } @@ -154,8 +178,10 @@ if ($cfg['download_page'] && !$password_challenged && !$button_download && !$but { require (JIRAFEAU_ROOT.'lib/template/header.php'); echo '
' . - '
'; ?> + ''; ?> ' . $link['file_name'] . '' . ''; echo '
' . @@ -164,14 +190,24 @@ if ($cfg['download_page'] && !$password_challenged && !$button_download && !$but '
' . t('By using our services, you accept of our'). ' ' . t('Term Of Service') . ''; ?>'; + document.getElementById('submit_download').submit ();"/>'; + document.getElementById('submit_preview').submit ();"/>
'; @@ -187,16 +223,41 @@ if (!jirafeau_is_viewable ($link['mime_type']) || !$cfg['preview'] || $button_do else header ('Content-Type: ' . $link['mime_type']); -/* Read file */ -$r = fopen (VAR_FILES . $p . $link['md5'], 'r'); -while (!feof ($r)) +/* Read encrypted file. */ +if ($link['crypted']) +{ + /* Extract key and iv. */ + $ex = explode (".", $crypt_key); + $key = $ex[0]; + $iv = base64_decode($ex[1]); + error_log ("crypt_key: " . $crypt_key . " iv: " . $v . " key: ". $key . "\n", 3, "debug.log"); + /* Init module */ + $m = mcrypt_module_open('rijndael-256', '', 'ofb', ''); + mcrypt_generic_init($m, $key, $iv); + /* Decrypt file. */ + $r = fopen (VAR_FILES . $p . $link['md5'], 'r'); + while (!feof ($r)) + { + $dec = mdecrypt_generic($m, fread ($r, 1024)); + print $dec; + ob_flush(); + } + fclose ($r); + /* Cleanup. */ + mcrypt_generic_deinit($m); + mcrypt_module_close($m); +} +/* Read file. */ +else { - print fread ($r, 1024); - ob_flush(); + $r = fopen (VAR_FILES . $p . $link['md5'], 'r'); + while (!feof ($r)) + { + print fread ($r, 1024); + ob_flush(); + } + fclose ($r); } -fclose ($r); - -//readfile (VAR_FILES . $p . $link['md5']); if ($link['onetime'] == 'O') jirafeau_delete_link ($link_name); diff --git a/lib/config.php b/lib/config.php index e80eefc..790ae16 100644 --- a/lib/config.php +++ b/lib/config.php @@ -47,6 +47,11 @@ $cfg['download_page'] = false; The scripting interface can propose to create, read, write, delete blocks of data. */ $cfg['enable_blocks'] = false; +/* Encryption feature. disabled by default. + * This feature needs to have mcrypt php module installed. + * Note: activating encryption will make file-deduplication to fail. + */ +$cfg['enable_crypt'] = false; if ((basename (__FILE__) != 'config.local.php') && file_exists (JIRAFEAU_ROOT.'lib/config.local.php')) diff --git a/lib/functions.js b/lib/functions.js index 2dcde55..2aa3bd6 100755 --- a/lib/functions.js +++ b/lib/functions.js @@ -16,13 +16,21 @@ * along with this program. If not, see . */ -function show_link (url, reference, delete_code, date) +function show_link (url, reference, delete_code, crypt_key, date) { var download_link = url + 'file.php?h=' + reference; - var delete_link = download_link + '&d=' + delete_code; - var delete_link_href = download_link + '&d=' + delete_code; + var download_link_href = url + 'file.php?h=' + reference; + if (crypt_key.length > 0) + { + download_link += '&k=' + crypt_key; + download_link_href += '&k=' + crypt_key; + } + + var delete_link = url + 'file.php?h=' + reference + '&d=' + delete_code; + var delete_link_href = url + 'file.php?h=' + reference + '&d=' + delete_code; + document.getElementById('upload_link').innerHTML = download_link; - document.getElementById('upload_link').href = download_link; + document.getElementById('upload_link').href = download_link_href; document.getElementById('delete_link').innerHTML = delete_link; document.getElementById('delete_link').href = delete_link_href; if (date) @@ -52,8 +60,11 @@ function upload_progress (e) /* Show the user the operation do not reach 100%, the server need time * to give a response before providing the link. */ - var p = Math.round (e.loaded * 99 / e.total); - show_upload_progression (p.toString() + '%'); + var p = Math.round (e.loaded * 100 / e.total); + if (p == 100) + show_upload_progression ('Finalizing ...'); + else + show_upload_progression (p.toString() + '%'); } function upload_failed (e) @@ -91,10 +102,10 @@ function classic_upload (url, file, time, password, one_time) d.setSeconds (d.getSeconds() + 2419200); else return; - show_link (url, res[0], res[1], d.toString()); + show_link (url, res[0], res[1], res[2], d.toString()); } else - show_link (url, res[0], res[1]); + show_link (url, res[0], res[1], res[2]); } } req.open ("POST", url + 'script.php' , true); @@ -167,8 +178,11 @@ function async_upload_progress (e) { if (!e.lengthComputable && async_global_file.size != 0) return; - var p = Math.round ((e.loaded + async_global_transfered) * 99 / (async_global_file.size)); - show_upload_progression (p.toString() + '%'); + var p = Math.round ((e.loaded + async_global_transfered) * 100 / (async_global_file.size)); + if (p == 100) + show_upload_progression ('Finalizing...'); + else + show_upload_progression (p.toString() + '%'); } function async_upload_push (code) @@ -197,7 +211,7 @@ function async_upload_push (code) } req.open ("POST", async_global_url + 'script.php?push_async' , true); - var chunk_size = parseInt (async_global_max_size * 0.90); + var chunk_size = parseInt (async_global_max_size * 0.50); var start = async_global_transfered; var end = start + chunk_size; if (end >= async_global_file.size) @@ -240,10 +254,10 @@ function async_upload_end (code) d.setSeconds (d.getSeconds() + 2419200); else return; - show_link (async_global_url, res[0], res[1], d.toString()); + show_link (async_global_url, res[0], res[1], res[2], d.toString()); } else - show_link (async_global_url, res[0], res[1]); + show_link (async_global_url, res[0], res[1], res[2]); } } req.open ("POST", async_global_url + 'script.php?end_async' , true); diff --git a/lib/functions.php b/lib/functions.php index 96f4c2f..c07b6c9 100755 --- a/lib/functions.php +++ b/lib/functions.php @@ -306,13 +306,14 @@ jirafeau_delete_file ($md5) * @param $key if not empty, protect the file with this key * @param $time the time of validity of the file * @param $ip uploader's ip + * @param $crypt boolean asking to crypt or not * @returns an array containing some information * 'error' => information on possible errors * 'link' => the link name of the uploaded file * 'delete_link' => the link code to delete file */ function -jirafeau_upload ($file, $one_time_download, $key, $time, $ip) +jirafeau_upload ($file, $one_time_download, $key, $time, $ip, $crypt) { if (empty ($file['tmp_name']) || !is_uploaded_file ($file['tmp_name'])) { @@ -327,6 +328,16 @@ jirafeau_upload ($file, $one_time_download, $key, $time, $ip) /* array representing no error */ $noerr = array ('has_error' => false, 'why' => ''); + /* Crypt file if option is enabled. */ + $crypted = false; + $crypt_key = ''; + if ($crypt == true && extension_loaded('mcrypt')) + { + $crypt_key = jirafeau_encrypt_file ($file['tmp_name'], $file['tmp_name']); + if (strlen($crypt_key) > 0) + $crypted = true; + } + /* file informations */ $md5 = md5_file ($file['tmp_name']); $name = str_replace (NL, '', trim ($file['name'])); @@ -370,7 +381,7 @@ jirafeau_upload ($file, $one_time_download, $key, $time, $ip) /* Create delete code. */ $delete_link_code = jirafeau_gen_random (8); - /* md5 password or empty */ + /* md5 password or empty. */ $password = ''; if (!empty ($key)) $password = md5 ($key); @@ -381,7 +392,7 @@ jirafeau_upload ($file, $one_time_download, $key, $time, $ip) fwrite ($handle, $name . NL. $mime_type . NL. $size . NL. $password . NL. $time . NL . $md5. NL . ($one_time_download ? 'O' : 'R') . NL . date ('U') . - NL . $ip . NL. $delete_link_code . NL); + NL . $ip . NL. $delete_link_code . NL . ($crypted ? 'C' : 'O')); fclose ($handle); $md5_link = base_16_to_64 (md5_file ($link_tmp_name)); $l = s2p ("$md5_link"); @@ -411,7 +422,8 @@ jirafeau_upload ($file, $one_time_download, $key, $time, $ip) } return (array ('error' => $noerr, 'link' => $md5_link, - 'delete_link' => $delete_link_code)); + 'delete_link' => $delete_link_code, + 'crypt_key' => $crypt_key)); } /** @@ -533,6 +545,8 @@ jirafeau_get_link ($hash) $out['upload_date'] = trim ($c[7]); $out['ip'] = trim ($c[8]); $out['link_code'] = trim ($c[9]); + if (trim ($c[10]) == 'C') + $out['crypted'] = true; return $out; } @@ -867,10 +881,11 @@ jirafeau_async_push ($ref, $data, $code) * Finalyze an asynchronous upload. * @param $ref asynchronous upload reference * @param $code client code for this operation + * @param $crypt boolean asking to crypt or not * @return a string containing the download reference followed by a delete code or the string "Error" */ function -jirafeau_async_end ($ref, $code) +jirafeau_async_end ($ref, $code, $crypt) { /* Get async infos. */ $a = jirafeau_get_async_ref ($ref); @@ -882,6 +897,16 @@ jirafeau_async_end ($ref, $code) $p = VAR_ASYNC . s2p ($ref) . $ref . "_data"; if (!file_exists($p)) return "Error"; + + $crypted = false; + $crypt_key = ''; + if ($crypt == true && extension_loaded('mcrypt')) + { + $cypt_key = jirafeau_encrypt_file ($p, $p); + if (strlen($crypt_key) > 0) + $crypted = true; + } + $md5 = md5_file ($p); $size = filesize($p); $np = s2p ($md5); @@ -911,7 +936,7 @@ jirafeau_async_end ($ref, $code) fwrite ($handle, $a['file_name'] . NL . $a['mime_type'] . NL . $size . NL . $a['key'] . NL . $a['time'] . NL . $md5 . NL . $a['onetime'] . NL . - date ('U') . NL . $a['ip'] . NL . $delete_link_code . NL); + date ('U') . NL . $a['ip'] . NL . $delete_link_code . NL . ($crypted ? 'C' : 'O')); fclose ($handle); $md5_link = base_16_to_64 (md5_file ($link_tmp_name)); $l = s2p ("$md5_link"); @@ -921,7 +946,7 @@ jirafeau_async_end ($ref, $code) /* Clean async upload. */ jirafeau_async_delete ($ref); - return $md5_link . NL . $delete_link_code; + return $md5_link . NL . $delete_link_code . NL . urlencode($crypt_key); } /** @@ -1264,5 +1289,80 @@ jirafeau_admin_clean_block () return $count; } +/** + * Crypt file and returns decrypt key. + * @param $fp_src file path to the file to crypt. + * @param $fp_dst file path to the file to write crypted file (could be the same). + * @return decrypt key composed of the key and the iv separated by a point ('.') + */ +function +jirafeau_encrypt_file ($fp_src, $fp_dst) +{ + $fs = filesize ($fp_src); + if ($fs === false || $fs == 0 || !extension_loaded('mcrypt')) + return ''; + + /* Prepare module. */ + $m = mcrypt_module_open('rijndael-256', '', 'ofb', ''); + $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($m), MCRYPT_DEV_URANDOM); + /* Generate key. */ + $ks = mcrypt_enc_get_key_size($m); + $key = substr(md5(jirafeau_gen_random(12)), 0, $ks); + /* Init module. */ + mcrypt_generic_init($m, $key, $iv); + /* Crypt file. */ + $r = fopen ($fp_src, 'r'); + $w = fopen ($fp_dst, 'c'); + while (!feof ($r)) + { + $enc = mcrypt_generic($m, fread ($r, 1024)); + if (fwrite ($w, $enc) === false) + return ''; + } + fclose ($r); + fclose ($w); + /* Cleanup. */ + mcrypt_generic_deinit($m); + mcrypt_module_close($m); + return $key . "." . base64_encode($iv); +} + +/** + * Decrypt file. + * @param $fp_src file path to the file to decrypt. + * @param $fp_dst file path to the file to write decrypted file (could be the same). + * @param $k string composed of the key and the iv separated by a point ('.') + * @return key used to decrypt. a string of length 0 is returned if failed. + */ +function +jirafeau_decrypt_file ($fp_src, $fp_dst, $k) +{ + $fs = filesize ($fp_src); + if ($fs === false || $fs == 0 || !extension_loaded('mcrypt')) + return false; + + /* Extract key and iv. */ + $ex = explode (".", $k); + $key = $ex[0]; + $iv = base64_decode($ex[1]); + /* Init module */ + $m = mcrypt_module_open('rijndael-256', '', 'ofb', ''); + mcrypt_generic_init($m, $key, $iv); + /* Decrypt file. */ + $r = fopen ($fp_src, 'r'); + $w = fopen ($fp_dst, 'c'); + while (!feof ($r)) + { + $dec = mdecrypt_generic($m, fread ($r, 1024)); + if (fwrite ($w, $dec) === false) + return false; + } + fclose ($r); + fclose ($w); + /* Cleanup. */ + mcrypt_generic_deinit($m); + mcrypt_module_close($m); + return true; +} ?> diff --git a/lib/lang.php b/lib/lang.php index 6e592a2..bf74e8d 100755 --- a/lib/lang.php +++ b/lib/lang.php @@ -54,4 +54,4 @@ function t ($text) return ($text); } -?> \ No newline at end of file +?> diff --git a/script.php b/script.php index a6b9457..7d5e25c 100755 --- a/script.php +++ b/script.php @@ -292,7 +292,8 @@ if (isset ($_FILES['file']) && is_writable (VAR_FILES) } $res = jirafeau_upload ($_FILES['file'], isset ($_POST['one_time_download']), - $key, $time, $_SERVER['REMOTE_ADDR']); + $key, $time, $_SERVER['REMOTE_ADDR'], + $cfg['enable_crypt']); if (empty($res) || $res['error']['has_error']) { @@ -301,9 +302,12 @@ if (isset ($_FILES['file']) && is_writable (VAR_FILES) } /* Print direct link. */ echo $res['link']; - echo NL; /* Print delete link. */ + echo NL; echo $res['delete_link']; + /* Print decrypt key. */ + echo NL; + echo urlencode($res['crypt_key']); } elseif (isset ($_GET['h'])) { @@ -580,7 +584,7 @@ elseif (isset ($_GET['end_async'])) || !isset ($_POST['code'])) echo "Error"; else - echo jirafeau_async_end ($_POST['ref'], $_POST['code']); + echo jirafeau_async_end ($_POST['ref'], $_POST['code'], $cfg['enable_crypt']); } /* Initialize block. */ elseif (isset ($_GET['init_block']) && $cfg['enable_blocks']) -- 2.34.1