diff options
-rw-r--r-- | INSTALL.txt | 21 | ||||
-rw-r--r-- | includes/file.inc | 8 | ||||
-rw-r--r-- | modules/upload.module | 78 | ||||
-rw-r--r-- | modules/upload/upload.module | 78 |
4 files changed, 169 insertions, 16 deletions
diff --git a/INSTALL.txt b/INSTALL.txt index fc2e5529a..dce0db9b3 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -68,8 +68,8 @@ INSTALLATION The default configuration can be found in the 'sites/default/settings.php' file within your Drupal installation. - Before you can run Drupal, you must set the database URL. Open the - configuration file and edit the $db_url line to match the database + Before you can run Drupal, you must set the database URL. Open the + configuration file and edit the $db_url line to match the database defined in the previous step: $db_url = "mysql://username:password@localhost/databasename"; @@ -144,12 +144,17 @@ INSTALLATION by the Drupal server process. You can change the name of this subdirectory at "Administer > Settings > File system settings". - SECURITY NOTICE: Certain Apache configurations can be vulnerable - to a security exploit allowing arbitrary code execution. Drupal - will attempt to automatically create a .htaccess file in your + SECURITY NOTICE: Certain Apache configurations can be vulnerable + to a security exploit allowing arbitrary code execution. Drupal + will attempt to automatically create a .htaccess file in your "files" directory to protect you. If you already have a .htaccess - file in that location, please add the following line: - SetHandler This_is_a_Drupal_security_line_do_not_remove + file in that location, please add the following lines: + + SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006 + Options None + <IfModule mod_rewrite.c> + RewriteEngine off + </IfModule> You can now launch your browser and point it to your Drupal site. @@ -186,7 +191,7 @@ example, set some general settings for your site with "Administer > Settings". Enable modules via "Administer > Modules". User permissions can be set with "Administer > Users > Configure > Permissions". -For more information on configuration options, read the +For more information on configuration options, read the instructions which accompany the different configuration settings and consult the various help pages available in the administration panel. diff --git a/includes/file.inc b/includes/file.inc index 802df5656..7d23d4a7c 100644 --- a/includes/file.inc +++ b/includes/file.inc @@ -113,17 +113,17 @@ function file_check_directory(&$directory, $mode = 0, $form_item = NULL) { } if ((file_directory_path() == $directory || file_directory_temp() == $directory) && !is_file("$directory/.htaccess")) { - if (($fp = fopen("$directory/.htaccess", 'w')) && fputs($fp, 'SetHandler This_is_a_Drupal_security_line_do_not_remove')) { + $htaccess_lines = "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nOptions None\n<IfModule mod_rewrite.c>\n RewriteEngine off\n</IfModule>"; + if (($fp = fopen("$directory/.htaccess", 'w')) && fputs($fp, $htaccess_lines)) { fclose($fp); } else { - $message = t("Security warning: Couldn't write .htaccess. Please create a .htaccess file in your %directory directory which contains the following line: <code>SetHandler This_is_a_Drupal_security_line_do_not_remove</code>", array('%directory' => $directory)); + $message = t("Security warning: Couldn't write .htaccess file. Please create a .htaccess file in your %directory directory which contains the following lines: <code>%htaccess</code>", array('%directory' => theme('placeholder', $directory), '%htaccess' => '<br />'. str_replace("\n", '<br />', check_plain($htaccess_lines)))); form_set_error($form_item, $message); - watchdog('file system', $message, WATCHDOG_ERROR); + watchdog('security', $message, WATCHDOG_ERROR); } } - return true; } diff --git a/modules/upload.module b/modules/upload.module index ca01ac700..98cd8c2fc 100644 --- a/modules/upload.module +++ b/modules/upload.module @@ -253,9 +253,10 @@ function _upload_prepare(&$node) { unset($_SESSION['file_current_upload']); + global $user; + // Save new file uploads to tmp dir. if (($file = file_check_upload()) && user_access('upload files')) { - global $user; // Scale image uploads. $file = _upload_image($file); @@ -274,6 +275,11 @@ function _upload_prepare(&$node) { // Attach file previews to node object. if (is_array($_SESSION['file_previews']) && count($_SESSION['file_previews'])) { foreach($_SESSION['file_previews'] as $fid => $file) { + if ($user->uid != 1) { + // Here something.php.pps becomes something.php_.pps + $file->filename = upload_munge_filename($file->filename, NULL, 0); + $file->description = $file->filename; + } $node->files[$fid] = $file; } } @@ -375,6 +381,11 @@ function _upload_validate(&$node) { form_set_error('upload', t('The selected file %name can not be attached to this post, because the disk quota of %quota has been reached.', array('%name' => theme('placeholder', $file->filename), '%quota' => theme('placeholder', format_size($usersize))))); $valid = FALSE; } + elseif (strlen($node->files[$fid]->filename) > 255) { + form_set_error('upload', t('The selected file %name can not be attached to this post, because the filename is too long.', array('%name' => theme('placeholder', $munged_filename)))); + $valid = FALSE; + } + if (!$valid) { unset($node->files[$fid], $_SESSION['file_previews'][$fid]); file_delete($file->filepath); @@ -522,6 +533,66 @@ function upload_total_space_used() { return db_result(db_query('SELECT SUM(filesize) FROM {files}')); } +/** + * Munge the filename as needed for security purposes. + * + * @param $filename + * The name of a file to modify. + * @param $extensions + * A space separated list of valid extensions. If this is blank, we'll use + * the admin-defined defaults for the user role from upload_extensions_$rid. + * @param $alerts + * Whether alerts (watchdog, drupal_set_message()) should be displayed. + * @return $filename + * The potentially modified $filename. + */ +function upload_munge_filename($filename, $extensions = NULL, $alerts = 1) { + global $user; + + $original = $filename; + + // Allow potentially insecure uploads for very savvy users and admin + if (!variable_get('allow_insecure_uploads', 0)) { + + if (!isset($extensions)) { + $extensions = ''; + foreach ($user->roles as $rid => $name) { + $extensions .= ' '. variable_get("upload_extensions_$rid", variable_get('upload_extensions_default', 'jpg jpeg gif png txt html doc xls pdf ppt pps')); + } + + } + + $whitelist = array_unique(explode(' ', trim($extensions))); + + $filename_parts = explode('.', $filename); + + $new_filename = array_shift($filename_parts); // Remove file basename. + $final_extension = array_pop($filename_parts); // Remove final extension. + + foreach($filename_parts as $filename_part) { + $new_filename .= ".$filename_part"; + if (!in_array($filename_part, $whitelist) && preg_match("/^[a-zA-Z]{2,5}\d?$/", $filename_part)) { + $new_filename .= '_'; + } + } + $filename = "$new_filename.$final_extension"; + } + + if ($alerts && $original != $filename) { + $message = t('Your filename has been renamed to conform to site policy.'); + drupal_set_message($message); + } + + return $filename; +} + +/** + * Undo the effect of upload_munge_filename(). + */ +function upload_unmunge_filename($filename) { + return str_replace('_.', '.', $filename); +} + function upload_save($node) { if (!is_array($node->files)) { return; @@ -609,14 +680,17 @@ function upload_delete_revision($node) { } function _upload_form($node) { + $form['#theme'] = 'upload_form_new'; if (is_array($node->files) && count($node->files)) { $form['files']['#theme'] = 'upload_form_current'; $form['files']['#tree'] = TRUE; foreach ($node->files as $key => $file) { - $description = "<small>". file_create_url((strpos($file->fid,'upload') === false ? $file->filepath : file_create_filename($file->filename, file_create_path()))) ."</small>"; + $description = file_create_url((strpos($file->fid, 'upload') === false ? $file->filepath : file_create_filename($file->filename, file_create_path()))); + $description = "<small>". check_plain($description) ."</small>"; $form['files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => (strlen($file->description)) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => $description ); + $form['files'][$key]['size'] = array('#type' => 'markup', '#value' => format_size($file->filesize)); $form['files'][$key]['remove'] = array('#type' => 'checkbox', '#default_value' => $file->remove); $form['files'][$key]['list'] = array('#type' => 'checkbox', '#default_value' => $file->list); diff --git a/modules/upload/upload.module b/modules/upload/upload.module index ca01ac700..98cd8c2fc 100644 --- a/modules/upload/upload.module +++ b/modules/upload/upload.module @@ -253,9 +253,10 @@ function _upload_prepare(&$node) { unset($_SESSION['file_current_upload']); + global $user; + // Save new file uploads to tmp dir. if (($file = file_check_upload()) && user_access('upload files')) { - global $user; // Scale image uploads. $file = _upload_image($file); @@ -274,6 +275,11 @@ function _upload_prepare(&$node) { // Attach file previews to node object. if (is_array($_SESSION['file_previews']) && count($_SESSION['file_previews'])) { foreach($_SESSION['file_previews'] as $fid => $file) { + if ($user->uid != 1) { + // Here something.php.pps becomes something.php_.pps + $file->filename = upload_munge_filename($file->filename, NULL, 0); + $file->description = $file->filename; + } $node->files[$fid] = $file; } } @@ -375,6 +381,11 @@ function _upload_validate(&$node) { form_set_error('upload', t('The selected file %name can not be attached to this post, because the disk quota of %quota has been reached.', array('%name' => theme('placeholder', $file->filename), '%quota' => theme('placeholder', format_size($usersize))))); $valid = FALSE; } + elseif (strlen($node->files[$fid]->filename) > 255) { + form_set_error('upload', t('The selected file %name can not be attached to this post, because the filename is too long.', array('%name' => theme('placeholder', $munged_filename)))); + $valid = FALSE; + } + if (!$valid) { unset($node->files[$fid], $_SESSION['file_previews'][$fid]); file_delete($file->filepath); @@ -522,6 +533,66 @@ function upload_total_space_used() { return db_result(db_query('SELECT SUM(filesize) FROM {files}')); } +/** + * Munge the filename as needed for security purposes. + * + * @param $filename + * The name of a file to modify. + * @param $extensions + * A space separated list of valid extensions. If this is blank, we'll use + * the admin-defined defaults for the user role from upload_extensions_$rid. + * @param $alerts + * Whether alerts (watchdog, drupal_set_message()) should be displayed. + * @return $filename + * The potentially modified $filename. + */ +function upload_munge_filename($filename, $extensions = NULL, $alerts = 1) { + global $user; + + $original = $filename; + + // Allow potentially insecure uploads for very savvy users and admin + if (!variable_get('allow_insecure_uploads', 0)) { + + if (!isset($extensions)) { + $extensions = ''; + foreach ($user->roles as $rid => $name) { + $extensions .= ' '. variable_get("upload_extensions_$rid", variable_get('upload_extensions_default', 'jpg jpeg gif png txt html doc xls pdf ppt pps')); + } + + } + + $whitelist = array_unique(explode(' ', trim($extensions))); + + $filename_parts = explode('.', $filename); + + $new_filename = array_shift($filename_parts); // Remove file basename. + $final_extension = array_pop($filename_parts); // Remove final extension. + + foreach($filename_parts as $filename_part) { + $new_filename .= ".$filename_part"; + if (!in_array($filename_part, $whitelist) && preg_match("/^[a-zA-Z]{2,5}\d?$/", $filename_part)) { + $new_filename .= '_'; + } + } + $filename = "$new_filename.$final_extension"; + } + + if ($alerts && $original != $filename) { + $message = t('Your filename has been renamed to conform to site policy.'); + drupal_set_message($message); + } + + return $filename; +} + +/** + * Undo the effect of upload_munge_filename(). + */ +function upload_unmunge_filename($filename) { + return str_replace('_.', '.', $filename); +} + function upload_save($node) { if (!is_array($node->files)) { return; @@ -609,14 +680,17 @@ function upload_delete_revision($node) { } function _upload_form($node) { + $form['#theme'] = 'upload_form_new'; if (is_array($node->files) && count($node->files)) { $form['files']['#theme'] = 'upload_form_current'; $form['files']['#tree'] = TRUE; foreach ($node->files as $key => $file) { - $description = "<small>". file_create_url((strpos($file->fid,'upload') === false ? $file->filepath : file_create_filename($file->filename, file_create_path()))) ."</small>"; + $description = file_create_url((strpos($file->fid, 'upload') === false ? $file->filepath : file_create_filename($file->filename, file_create_path()))); + $description = "<small>". check_plain($description) ."</small>"; $form['files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => (strlen($file->description)) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => $description ); + $form['files'][$key]['size'] = array('#type' => 'markup', '#value' => format_size($file->filesize)); $form['files'][$key]['remove'] = array('#type' => 'checkbox', '#default_value' => $file->remove); $form['files'][$key]['list'] = array('#type' => 'checkbox', '#default_value' => $file->list); |