Commit 145e1ced authored by webchick's avatar webchick
Browse files

#319467: SA-2008-47 (#295053): Arbitrary file uploads in Blog API.

parent acd5e952
<?php
// $Id$
/**
* Implementation of hook_install().
*/
function blogapi_install() {
// Create tables.
drupal_install_schema('blogapi');
}
/**
* Implementation of hook_uninstall().
*/
function blogapi_uninstall() {
// Remove tables.
drupal_uninstall_schema('blogapi');
}
/**
* Implementation of hook_schema().
*/
function blogapi_schema() {
$schema['blogapi_files'] = array(
'description' => t('Stores information for files uploaded via the blogapi.'),
'fields' => array(
'fid' => array(
'description' => t('Primary Key: Unique file ID.'),
'type' => 'serial',
),
'uid' => array(
'description' => t('The {users}.uid of the user who is associated with the file.'),
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'filepath' => array(
'description' => t('Path of the file relative to Drupal root.'),
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'filesize' => array(
'description' => t('The size of the file in bytes.'),
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
),
'primary key' => array('fid'),
'indexes' => array(
'uid' => array('uid'),
),
);
return $schema;
}
/**
* @defgroup updates-5.x-to-6.x Blog API updates from 5.x to 6.x
* @{
......@@ -14,6 +73,22 @@ function blogapi_update_6000() {
return array();
}
/**
* Add blogapi_files table to enable size restriction for BlogAPI file uploads.
*
* This table was introduced in Drupal 6.4.
*/
function blogapi_update_6001() {
$ret = array();
if (!db_table_exists('blogapi_files')) {
$schema = blogapi_schema();
db_create_table($ret, 'blogapi_files', $schema['blogapi_files']);
}
return $ret;
}
/**
* @} End of "defgroup updates-5.x-to-6.x"
* The next series of updates should start at 7000.
......
......@@ -419,19 +419,66 @@ function blogapi_metaweblog_new_media_object($blogid, $username, $password, $fil
return blogapi_error($user);
}
$usersize = 0;
$uploadsize = 0;
$roles = array_intersect(user_roles(FALSE, 'administer content with blog api'), $user->roles);
foreach ($roles as $rid => $name) {
$extensions .= ' ' . strtolower(variable_get("blogapi_extensions_$rid", variable_get('blogapi_extensions_default', 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp')));
$usersize = max($usersize, variable_get("blogapi_usersize_$rid", variable_get('blogapi_usersize_default', 1)) * 1024 * 1024);
$uploadsize = max($uploadsize, variable_get("blogapi_uploadsize_$rid", variable_get('blogapi_uploadsize_default', 1)) * 1024 * 1024);
}
$filesize = strlen($file['bits']);
if ($filesize > $uploadsize) {
return blogapi_error(t('It is not possible to upload the file, because it exceeded the maximum filesize of @maxsize.', array('@maxsize' => format_size($uploadsize))));
}
if (_blogapi_space_used($user->uid) + $filesize > $usersize) {
return blogapi_error(t('The file can not be attached to this post, because the disk quota of @quota has been reached.', array('@quota' => format_size($usersize))));
}
// Only allow files with whitelisted extensions and convert remaining dots to
// underscores to prevent attacks via non-terminal executable extensions with
// files such as exploit.php.jpg.
$whitelist = array_unique(explode(' ', trim($extensions)));
$name = basename($file['name']);
if ($extension_position = strrpos($name, '.')) {
$filename = drupal_substr($name, 0, $extension_position);
$final_extension = drupal_substr($name, $extension_position + 1);
if (!in_array(strtolower($final_extension), $whitelist)) {
return blogapi_error(t('It is not possible to upload the file, because it is only possible to upload files with the following extensions: @extensions', array('@extensions' => implode(' ', $whitelist))));
}
$filename = str_replace('.', '_', $filename);
$filename .= '.' . $final_extension;
}
$data = $file['bits'];
if (!$data) {
return blogapi_error(t('No file sent.'));
}
if (!$filepath = file_unmanaged_save_data($data, $name)) {
if (!$file = file_unmanaged_save_data($data, $filename)) {
return blogapi_error(t('Error storing file.'));
}
$row = new stdClass();
$row->uid = $user->uid;
$row->filepath = $file;
$row->filesize = $filesize;
drupal_write_record('blogapi_files', $row);
// Return the successful result.
return array('url' => file_create_url($filepath), 'struct');
return array('url' => file_create_url($file), 'struct');
}
/**
* Blogging API callback. Returns a list of the taxonomy terms that can be
......@@ -683,6 +730,81 @@ function blogapi_admin_settings() {
'#description' => t('Select the content types available to external blogging clients via Blog API. If supported, each enabled content type will be displayed as a separate "blog" by the external client.')
);
$blogapi_extensions_default = variable_get('blogapi_extensions_default', 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp');
$blogapi_uploadsize_default = variable_get('blogapi_uploadsize_default', 1);
$blogapi_usersize_default = variable_get('blogapi_usersize_default', 1);
$form['settings_general'] = array(
'#type' => 'fieldset',
'#title' => t('File settings'),
'#collapsible' => TRUE,
);
$form['settings_general']['blogapi_extensions_default'] = array(
'#type' => 'textfield',
'#title' => t('Default permitted file extensions'),
'#default_value' => $blogapi_extensions_default,
'#maxlength' => 255,
'#description' => t('Default extensions that users can upload. Separate extensions with a space and do not include the leading dot.'),
);
$form['settings_general']['blogapi_uploadsize_default'] = array(
'#type' => 'textfield',
'#title' => t('Default maximum file size per upload'),
'#default_value' => $blogapi_uploadsize_default,
'#size' => 5,
'#maxlength' => 5,
'#description' => t('The default maximum file size a user can upload.'),
'#field_suffix' => t('MB')
);
$form['settings_general']['blogapi_usersize_default'] = array(
'#type' => 'textfield',
'#title' => t('Default total file size per user'),
'#default_value' => $blogapi_usersize_default,
'#size' => 5,
'#maxlength' => 5,
'#description' => t('The default maximum size of all files a user can have on the site.'),
'#field_suffix' => t('MB')
);
$form['settings_general']['upload_max_size'] = array('#value' => '<p>'. t('Your PHP settings limit the maximum file size per upload to %size.', array('%size' => format_size(file_upload_max_size()))).'</p>');
$roles = user_roles(FALSE, 'administer content with blog api');
$form['roles'] = array('#type' => 'value', '#value' => $roles);
foreach ($roles as $rid => $role) {
$form['settings_role_' . $rid] = array(
'#type' => 'fieldset',
'#title' => t('Settings for @role', array('@role' => $role)),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['settings_role_' . $rid]['blogapi_extensions_' . $rid] = array(
'#type' => 'textfield',
'#title' => t('Permitted file extensions'),
'#default_value' => variable_get('blogapi_extensions_' . $rid, $blogapi_extensions_default),
'#maxlength' => 255,
'#description' => t('Extensions that users in this role can upload. Separate extensions with a space and do not include the leading dot.'),
);
$form['settings_role_' . $rid]['blogapi_uploadsize_' . $rid] = array(
'#type' => 'textfield',
'#title' => t('Maximum file size per upload'),
'#default_value' => variable_get('blogapi_uploadsize_' . $rid, $blogapi_uploadsize_default),
'#size' => 5,
'#maxlength' => 5,
'#description' => t('The maximum size of a file a user can upload (in megabytes).'),
);
$form['settings_role_' . $rid]['blogapi_usersize_' . $rid] = array(
'#type' => 'textfield',
'#title' => t('Total file size per user'),
'#default_value' => variable_get('blogapi_usersize_' . $rid, $blogapi_usersize_default),
'#size' => 5,
'#maxlength' => 5,
'#description' => t('The maximum size of all files a user can have on the site (in megabytes).'),
);
}
return system_settings_form($form);
}
......@@ -845,3 +967,8 @@ function _blogapi_get_node_types() {
return $types;
}
function _blogapi_space_used($uid) {
return db_query('SELECT SUM(filesize) FROM {blogapi_files} f WHERE f.uid = :uid', array(':uid' => $uid))->fetchField();
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment