summaryrefslogtreecommitdiffstats
path: root/application
diff options
context:
space:
mode:
Diffstat (limited to 'application')
-rw-r--r--application/config/config.php2
-rw-r--r--application/controllers/file.php86
-rw-r--r--application/controllers/user.php8
-rw-r--r--application/core/MY_Controller.php64
-rwxr-xr-xapplication/errors/error_general.php10
-rw-r--r--application/helpers/filebin_helper.php20
-rw-r--r--application/models/mfile.php1
-rw-r--r--application/models/muser.php35
-rw-r--r--application/views/file/client.php4
-rw-r--r--application/views/file/deleted.php9
-rw-r--r--application/views/file/too_big.php3
-rw-r--r--application/views/file/upload_error.php6
-rw-r--r--application/views/file/upload_form.php2
-rw-r--r--application/views/file_plaintext/client.php4
-rw-r--r--application/views/file_plaintext/too_big.php2
-rw-r--r--application/views/file_plaintext/upload_error.php2
16 files changed, 175 insertions, 83 deletions
diff --git a/application/config/config.php b/application/config/config.php
index dda82de97..4aadac68d 100644
--- a/application/config/config.php
+++ b/application/config/config.php
@@ -293,7 +293,7 @@ $config['global_xss_filtering'] = FALSE;
| 'csrf_cookie_name' = The cookie name
| 'csrf_expire' = The number in seconds the token should expire.
*/
-$config['csrf_protection'] = FALSE;
+$config['csrf_protection'] = FALSE; // our controller enables this later
$config['csrf_token_name'] = 'csrf_test_name';
$config['csrf_cookie_name'] = 'csrf_cookie_name';
$config['csrf_expire'] = 7200;
diff --git a/application/controllers/file.php b/application/controllers/file.php
index ef2e87084..6e660b306 100644
--- a/application/controllers/file.php
+++ b/application/controllers/file.php
@@ -9,6 +9,12 @@
class File extends MY_Controller {
+ protected $json_enabled_functions = array(
+ "upload_history",
+ "do_upload",
+ "do_delete",
+ );
+
function __construct()
{
parent::__construct();
@@ -280,6 +286,10 @@ class File extends MY_Controller {
}
}
+ if (request_type() == "json") {
+ return send_json_reply($this->data["urls"]);
+ }
+
if (is_cli_client()) {
$redirect = false;
}
@@ -456,7 +466,7 @@ class File extends MY_Controller {
ORDER BY date $order
", array($user))->result_array();
- if ($this->input->get("json") !== false) {
+ if (request_type() == "json") {
return send_json_reply($query);
}
@@ -499,11 +509,11 @@ class File extends MY_Controller {
$ids = $this->input->post("ids");
$errors = array();
- $msgs = array();
+ $deleted = array();
$deleted_count = 0;
$total_count = 0;
- if (!$ids) {
+ if (!$ids || !is_array($ids)) {
show_error("No IDs specified");
}
@@ -511,20 +521,34 @@ class File extends MY_Controller {
$total_count++;
if (!$this->mfile->id_exists($id)) {
- $errors[] = "'$id' didn't exist anymore.";
+ $errors[] = array(
+ "id" => $id,
+ "reason" => "doesn't exist",
+ );
continue;
}
if ($this->mfile->delete_id($id)) {
- $msgs[] = "'$id' has been removed.";
+ $deleted[] = $id;
$deleted_count++;
} else {
- $errors[] = "'$id' couldn't be deleted.";
+ $errors[] = array(
+ "id" => $id,
+ "reason" => "unknown error",
+ );
}
}
+ if (request_type() == "json") {
+ return send_json_reply(array(
+ "errors" => $errors,
+ "deleted" => $deleted,
+ "total_count" => $total_count,
+ "deleted_count" => $deleted_count,
+ ));
+ }
+
$this->data["errors"] = $errors;
- $this->data["msgs"] = $msgs;
$this->data["deleted_count"] = $deleted_count;
$this->data["total_count"] = $total_count;
@@ -538,17 +562,14 @@ class File extends MY_Controller {
$this->muser->require_access("apikey");
if (!is_cli_client()) {
- echo "Not a listed cli client, please use the history to delete uploads.\n";
- return;
+ show_error("Not a listed cli client, please use the history to delete uploads.\n", 403);
}
$id = $this->uri->segment(3);
$this->data["id"] = $id;
if ($id && !$this->mfile->id_exists($id)) {
- $this->output->set_status_header(404);
- echo "Unknown ID '$id'.\n";
- return;
+ show_error("Unknown ID '$id'.", 404);
}
if ($this->mfile->delete_id($id)) {
@@ -562,6 +583,7 @@ class File extends MY_Controller {
function do_paste()
{
// desktop clients get a cookie to claim the ID later
+ // don't force them to log in just yet
if (is_cli_client()) {
$this->muser->require_access();
}
@@ -571,20 +593,11 @@ class File extends MY_Controller {
$filename = "stdin";
if (!$content) {
- $this->output->set_status_header(400);
- $this->data["msg"] = "Nothing was pasted, content is empty.";
- $this->load->view('header', $this->data);
- $this->load->view($this->var->view_dir.'/upload_error', $this->data);
- $this->load->view('footer');
- return;
+ show_error("Nothing was pasted, content is empty.", 400);
}
if ($filesize > $this->config->item('upload_max_size')) {
- $this->output->set_status_header(413);
- $this->load->view('header', $this->data);
- $this->load->view($this->var->view_dir.'/too_big');
- $this->load->view('footer');
- return;
+ show_error("Error while uploading: File too big", 413);
}
$limits = $this->muser->get_upload_id_limits();
@@ -605,6 +618,7 @@ class File extends MY_Controller {
function do_upload()
{
// desktop clients get a cookie to claim the ID later
+ // don't force them to log in just yet
if (is_cli_client()) {
$this->muser->require_access("apikey");
}
@@ -624,8 +638,6 @@ class File extends MY_Controller {
foreach ($files as $key => $file) {
// getNormalizedFILES() removes any file with error == 4
if ($file['error'] !== UPLOAD_ERR_OK) {
- $this->output->set_status_header(400);
-
// ERR_OK only for completeness, condition above ignores it
$errors = array(
UPLOAD_ERR_OK => "There is no error, the file uploaded with success",
@@ -638,27 +650,20 @@ class File extends MY_Controller {
UPLOAD_ERR_EXTENSION => "A PHP extension stopped the file upload",
);
- $this->data["msg"] = "Unknown error.";
+ $msg = "Unknown error.";
if (isset($errors[$file['error']])) {
- $this->data["msg"] = $errors[$file['error']];
+ $msg = $errors[$file['error']];
} else {
- $this->data["msg"] = "Unknown error code: ".$file['error'].". Please report a bug.";
+ $msg = "Unknown error code: ".$file['error'].". Please report a bug.";
}
- $this->load->view('header', $this->data);
- $this->load->view($this->var->view_dir.'/upload_error', $this->data);
- $this->load->view('footer');
- return;
+ show_error("Error while uploading: ".$msg, 400);
}
$filesize = filesize($file['tmp_name']);
if ($filesize > $this->config->item('upload_max_size')) {
- $this->output->set_status_header(413);
- $this->load->view('header', $this->data);
- $this->load->view($this->var->view_dir.'/too_big');
- $this->load->view('footer');
- return;
+ show_error("Error while uploading: File too big", 413);
}
}
@@ -699,9 +704,16 @@ class File extends MY_Controller {
$this->muser->require_access();
$last_upload = $this->session->userdata("last_upload");
+
+ if ($last_upload === false) {
+ show_error("Failed to get last upload data");
+ }
+
$ids = $last_upload["ids"];
$errors = array();
+ assert(is_array($ids));
+
foreach ($ids as $key => $id) {
$filedata = $this->mfile->get_filedata($id);
diff --git a/application/controllers/user.php b/application/controllers/user.php
index 498a626d7..56f571d6a 100644
--- a/application/controllers/user.php
+++ b/application/controllers/user.php
@@ -8,6 +8,10 @@
*/
class User extends MY_Controller {
+ protected $json_enabled_functions = array(
+ "apikeys",
+ );
+
function __construct()
{
@@ -127,6 +131,10 @@ class User extends MY_Controller {
WHERE `user` = ? order by created desc
", array($userid))->result_array();
+ if (request_type() == "json") {
+ return send_json_reply($query);
+ }
+
$this->data["query"] = $query;
$this->load->view('header', $this->data);
diff --git a/application/core/MY_Controller.php b/application/core/MY_Controller.php
index 278768ad2..312b0f763 100644
--- a/application/core/MY_Controller.php
+++ b/application/core/MY_Controller.php
@@ -11,7 +11,7 @@ class MY_Controller extends CI_Controller {
public $data = array();
public $var;
- private $json_enabled_functions = array(
+ protected $json_enabled_functions = array(
);
function __construct()
@@ -19,6 +19,7 @@ class MY_Controller extends CI_Controller {
parent::__construct();
$this->var = new StdClass();
+ $csrf_protection = true;
$this->load->library('migration');
if ( ! $this->migration->current()) {
@@ -31,6 +32,67 @@ class MY_Controller extends CI_Controller {
mb_internal_encoding('UTF-8');
$this->load->helper(array('form', 'filebin'));
+ // TODO: proper accept header handling or is this enough?
+ if (isset($_SERVER["HTTP_ACCEPT"])) {
+ if ($_SERVER["HTTP_ACCEPT"] == "application/json") {
+ request_type("json");
+ }
+ }
+
+ // Allow for easier testing in browser
+ if ($this->input->get("json") !== false) {
+ request_type("json");
+ }
+
+ if (request_type() == "json" && ! in_array($this->uri->rsegment(2), $this->json_enabled_functions)) {
+ show_error("Function not JSON enabled");
+ }
+
+ if ($this->input->post("apikey") !== false) {
+ /* This relies on the authentication code always verifying the supplied
+ * apikey. If the key is not verified/logged in an attacker could simply
+ * add an empty "apikey" field to the CSRF form to circumvent the
+ * protection. If we always log in if a key is supplied we can ensure
+ * that an attacker (and the victim since they get a cookie) can only
+ * access the attacker's account.
+ */
+ $csrf_protection = false;
+ }
+
+ $uri_start = $this->uri->rsegment(1)."/".$this->uri->rsegment(2);
+ $csrf_whitelisted_handlers = array(
+ "always" => array(
+ /* Whitelist the upload pages because they don't cause harm and a user
+ * might keep the upload page open for more than csrf_expire seconds
+ * and we don't want to annoy them when they upload a big file and the
+ * CSRF check fails.
+ */
+ "file/do_upload",
+ "file/do_paste",
+ ),
+ "cli_client" => array(
+ "file/do_delete",
+ "file/delete",
+ "file/upload_history",
+ ),
+ );
+ if (in_array($uri_start, $csrf_whitelisted_handlers["always"])) {
+ $csrf_protection = false;
+ }
+
+ // TODO: replace cli client with request_type("plain")?
+ if (is_cli_client() && in_array($uri_start, $csrf_whitelisted_handlers["cli_client"])) {
+ $csrf_protection = false;
+ }
+
+ if ($csrf_protection && !$this->input->is_cli_request()) {
+ // 2 functions for accessing config options, really?
+ $this->config->set_item('csrf_protection', true);
+ config_item("csrf_protection", true);
+ $this->security->__construct();
+ $this->security->csrf_verify();
+ }
+
$this->data['title'] = "FileBin";
}
}
diff --git a/application/errors/error_general.php b/application/errors/error_general.php
index da3999cbd..fc3d3f607 100755
--- a/application/errors/error_general.php
+++ b/application/errors/error_general.php
@@ -9,6 +9,16 @@ if (class_exists("CI_Controller") && !isset($GLOBALS["is_error_page"])) {
$CI->load->helper("filebin");
$CI->load->helper("url");
+ if (request_type() == "json") {
+ $array = array(
+ "status" => "error",
+ "message" => strip_tags($message),
+ );
+ header('Content-type: application/json');
+ echo json_encode($array);
+ exit();
+ }
+
if (is_cli_client()) {
$message = strip_tags($message);
echo "$heading: $message\n";
diff --git a/application/helpers/filebin_helper.php b/application/helpers/filebin_helper.php
index 4af106a14..9b124506f 100644
--- a/application/helpers/filebin_helper.php
+++ b/application/helpers/filebin_helper.php
@@ -326,14 +326,30 @@ function auth_driver_function_implemented($function)
function user_logged_in()
{
$CI =& get_instance();
+ $CI->load->model("muser");
return $CI->muser->logged_in();
}
-function send_json_reply($array)
+function send_json_reply($array, $status = "success")
{
+ $reply = array();
+ $reply["status"] = $status;
+ $reply["data"] = $array;
+
$CI =& get_instance();
$CI->output->set_content_type('application/json');
- $CI->output->set_output(json_encode($array));
+ $CI->output->set_output(json_encode($reply));
+}
+
+function request_type($set_type = null)
+{
+ static $type = null;
+
+ if ($set_type !== null) {
+ $type = $set_type;
+ }
+
+ return $type;
}
# vim: set noet:
diff --git a/application/models/mfile.php b/application/models/mfile.php
index fe762d954..e862f1930 100644
--- a/application/models/mfile.php
+++ b/application/models/mfile.php
@@ -312,7 +312,6 @@ class Mfile extends CI_Model {
function delete_id($id)
{
- $this->muser->require_access("apikey");
$filedata = $this->get_filedata($id);
$userid = $this->muser->get_userid();
diff --git a/application/models/muser.php b/application/models/muser.php
index 843b7cad6..b3c16bf78 100644
--- a/application/models/muser.php
+++ b/application/models/muser.php
@@ -67,14 +67,6 @@ class Muser extends CI_Model {
{
$username = $this->input->post("username");
$password = $this->input->post("password");
- $apikey = $this->input->post("apikey");
-
- if ($apikey !== false) {
- if ($this->apilogin(trim($apikey))) {
- return true;
- }
- show_error("API key login failed", 401);
- }
// prefer post parameters if either (username or password) is set
if ($username === false && $password === false) {
@@ -84,19 +76,24 @@ class Muser extends CI_Model {
}
}
- if ($apikey === false && $username !== false && $password !== false) {
+ if ($username !== false && $password !== false) {
if ($this->login($username, $password)) {
return true;
} else {
show_error("Login failed", 401);
}
}
+
+ return null;
}
function apilogin($apikey)
{
$this->require_session();
+ // get rid of spaces and newlines
+ $apikey = trim($apikey);
+
$query = $this->db->query("
SELECT a.user userid
FROM apikeys a
@@ -111,7 +108,7 @@ class Muser extends CI_Model {
return true;
}
- return false;
+ show_error("API key login failed", 401);
}
function logout()
@@ -168,22 +165,26 @@ class Muser extends CI_Model {
return true;
}
- show_error("Access denied", 403);
+ show_error("Access denied: Access level too low", 403);
}
function require_access($wanted_level = "full")
{
+ if ($this->input->post("apikey") !== false) {
+ $this->apilogin($this->input->post("apikey"));
+ }
+
+ if (is_cli_client()) {
+ $this->login_cli_client();
+ }
+
if ($this->logged_in()) {
return $this->check_access_level($wanted_level);
}
+ // if a CLI client reaches this point it failed to log in
if (is_cli_client()) {
- if ($this->login_cli_client()) {
- return $this->check_access_level($wanted_level);
- }
-
- echo "FileBin requires you to have an account, please go to the homepage for more information.\n";
- exit();
+ show_error("Not authenticated. FileBin requires you to have an account, please go to the homepage for more information.\n", 401);
}
// desktop clients get redirected to the login form
diff --git a/application/views/file/client.php b/application/views/file/client.php
index 5e141f141..29e254a80 100644
--- a/application/views/file/client.php
+++ b/application/views/file/client.php
@@ -42,7 +42,7 @@ machine <?php echo $domain; ?> login my_username password my_secret_password
<h1>Shell</h1>
<pre>
-curl -n -F "file=@/home/user/foo" <?php echo site_url(); ?> (binary safe)
-cat file | curl -n -F "file=@-;filename=stdin" <?php echo site_url(); ?> (binary safe)
+curl -n -F "file=@/home/user/foo" <?php echo site_url("file/do_upload"); ?> (binary safe)
+cat file | curl -n -F "file=@-;filename=stdin" <?php echo site_url("file/do_upload"); ?> (binary safe)
</pre>
diff --git a/application/views/file/deleted.php b/application/views/file/deleted.php
index ef02398d9..8a5818f2d 100644
--- a/application/views/file/deleted.php
+++ b/application/views/file/deleted.php
@@ -1,12 +1,9 @@
<div class="center">
<?php if (!empty($errors)) {
echo "<p>";
- echo implode("<br />\n", $errors);
- echo "</p>";
- } ?>
- <?php if (!empty($msgs)) {
- echo "<p>";
- echo implode("<br />\n", $msgs);
+ foreach ($errors as $error) {
+ echo "${error["id"]}: ${error["reason"]}<br>\n";
+ }
echo "</p>";
} ?>
diff --git a/application/views/file/too_big.php b/application/views/file/too_big.php
deleted file mode 100644
index 5b970a4c8..000000000
--- a/application/views/file/too_big.php
+++ /dev/null
@@ -1,3 +0,0 @@
-<div class="center">
- <p>Sorry, the file you uploaded is too big.</p>
-</div>
diff --git a/application/views/file/upload_error.php b/application/views/file/upload_error.php
deleted file mode 100644
index 83968eec2..000000000
--- a/application/views/file/upload_error.php
+++ /dev/null
@@ -1,6 +0,0 @@
-<div class="center">
- <p>
- An error occurred while uploading.<br />
- <?php echo $msg; ?>
- </p>
-</div>
diff --git a/application/views/file/upload_form.php b/application/views/file/upload_form.php
index 841ac3746..21a2cc4e6 100644
--- a/application/views/file/upload_form.php
+++ b/application/views/file/upload_form.php
@@ -46,7 +46,7 @@
<?php } else { ?>
<?php echo form_open('user/login'); ?>
- <input type="text" name="username" placeholder="Username" />
+ <input type="text" name="username" placeholder="Username" autofocus />
<input type="password" name="password" placeholder="Password" />
<input type="submit" class="btn btn-primary" value="Login" name="process" style="margin-bottom: 9px" />
<?php if(auth_driver_function_implemented("can_reset_password")) { ?>
diff --git a/application/views/file_plaintext/client.php b/application/views/file_plaintext/client.php
index b37fd81bd..0ab556df2 100644
--- a/application/views/file_plaintext/client.php
+++ b/application/views/file_plaintext/client.php
@@ -1,6 +1,6 @@
Shell (binary safe):
- curl -n -F "file=@/home/user/foo" <?php echo site_url()."\n"; ?>
- cat file | curl -n -F "file=@-;filename=stdin" <?php echo site_url()."\n"; ?>
+ curl -n -F "file=@/home/user/foo" <?php echo site_url("file/do_upload")."\n"; ?>
+ cat file | curl -n -F "file=@-;filename=stdin" <?php echo site_url("file/do_upload")."\n"; ?>
Client:
Development (git): http://git.server-speed.net/users/flo/fb
diff --git a/application/views/file_plaintext/too_big.php b/application/views/file_plaintext/too_big.php
deleted file mode 100644
index d27a0295c..000000000
--- a/application/views/file_plaintext/too_big.php
+++ /dev/null
@@ -1,2 +0,0 @@
-Sorry, the file you uploaded is too big.
-
diff --git a/application/views/file_plaintext/upload_error.php b/application/views/file_plaintext/upload_error.php
deleted file mode 100644
index c86c56911..000000000
--- a/application/views/file_plaintext/upload_error.php
+++ /dev/null
@@ -1,2 +0,0 @@
-An error occurred while uploading. <?php echo $msg; ?>
-