Index: INSTALL.txt =================================================================== RCS file: /cvs/drupal/drupal/INSTALL.txt,v retrieving revision 1.29.2.2 diff -u -p -r1.29.2.2 INSTALL.txt --- INSTALL.txt 25 May 2006 01:34:20 -0000 1.29.2.2 +++ INSTALL.txt 1 Jun 2006 21:31:02 -0000 @@ -147,9 +147,14 @@ INSTALLATION 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 + "files" directory to protect you. If you already have a .htaccess + file in that location, please add the following lines: + + SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006 + Options None + + RewriteEngine off + You can now launch your browser and point it to your Drupal site. Index: includes/file.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/file.inc,v retrieving revision 1.74.2.1 diff -u -p -r1.74.2.1 file.inc --- includes/file.inc 25 May 2006 01:34:20 -0000 1.74.2.1 +++ includes/file.inc 1 Jun 2006 21:31:03 -0000 @@ -113,17 +113,17 @@ function file_check_directory(&$director } 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\n RewriteEngine off\n"; + 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: SetHandler This_is_a_Drupal_security_line_do_not_remove", 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: %htaccess", array('%directory' => theme('placeholder', $directory), '%htaccess' => '
'. str_replace("\n", '
', check_plain($htaccess_lines)))); form_set_error($form_item, $message); - watchdog('file system', $message, WATCHDOG_ERROR); + watchdog('security', $message, WATCHDOG_ERROR); } } - return true; } Index: modules/upload.module =================================================================== RCS file: /cvs/drupal/drupal/modules/upload.module,v retrieving revision 1.100 diff -u -p -r1.100 upload.module --- modules/upload.module 27 Apr 2006 19:32:54 -0000 1.100 +++ modules/upload.module 1 Jun 2006 21:31:03 -0000 @@ -195,9 +195,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); @@ -216,6 +217,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; } } @@ -317,6 +323,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); @@ -464,6 +475,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; @@ -551,14 +622,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 = "". file_create_url((strpos($file->fid,'upload') === false ? $file->filepath : file_create_filename($file->filename, file_create_path()))) .""; + $description = file_create_url((strpos($file->fid, 'upload') === false ? $file->filepath : file_create_filename($file->filename, file_create_path()))); + $description = "". check_plain($description) .""; $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);