Index: INSTALL.txt =================================================================== RCS file: /cvs/drupal/drupal/INSTALL.txt,v retrieving revision 1.17.2.4 diff -u -p -r1.17.2.4 INSTALL.txt --- INSTALL.txt 25 May 2006 01:34:05 -0000 1.17.2.4 +++ INSTALL.txt 1 Jun 2006 20:27:13 -0000 @@ -189,12 +189,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 - "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 + 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 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.39.2.13 diff -u -p -r1.39.2.13 file.inc --- includes/file.inc 25 May 2006 01:34:05 -0000 1.39.2.13 +++ includes/file.inc 1 Jun 2006 20:27:16 -0000 @@ -106,13 +106,14 @@ function file_check_directory(&$director } if ((variable_get('file_directory_temp', FILE_DIRECTORY_TEMP) == $directory || variable_get('file_directory_path', 'files') == $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); } } Index: modules/upload.module =================================================================== RCS file: /cvs/drupal/drupal/modules/upload.module,v retrieving revision 1.31.2.10 diff -u -p -r1.31.2.10 upload.module --- modules/upload.module 20 Feb 2006 17:24:57 -0000 1.31.2.10 +++ modules/upload.module 1 Jun 2006 20:27:17 -0000 @@ -179,6 +179,11 @@ function upload_nodeapi(&$node, $op, $ar // Don't do any checks for uid #1. if ($user->uid != 1) { + // here, something.php.pps becomes something.php_.pps + $munged_filename = upload_munge_filename($file->filename, NULL, 0); + $file->filename = $munged_filename; + $file->description = $munged_filename; + // Validate file against all users roles. Only denies an upload when // all roles prevent it. foreach ($user->roles as $rid => $name) { @@ -211,6 +216,9 @@ function upload_nodeapi(&$node, $op, $ar elseif ($error['usersize'] == count($user->roles) && $user->uid != 1) { form_set_error('upload', t('Error attaching file %name: exceeds maximum file size', array('%name' => theme('placeholder', $file->filename)))); } + elseif (strlen($munged_filename) > 255) { + form_set_error('upload', t('The selected file %name can not be attached to this post, because the filename is to long.', array('%name' => theme('placeholder', $munged_filename)))); + } else { $key = 'upload_'. count($_SESSION['file_uploads']); $file->source = $key; @@ -321,6 +329,69 @@ function upload_count_size($uid = 0) { return db_result($result); } +/** + * 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) { + + $original = $filename; + + // Allow potentially insecure uploads for very savvy users + if (!variable_get('allow_insecure_uploads', 0)) { + + global $user; + + 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 the extension is not allowed and contains 2-5 characters followed + // by an optional digit, then it's potentially harmful, so we add an _ + 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) { foreach ((array)$node->files as $key => $file) { if ($file->source && !$file->remove) {