Commit message: None
CVE: CVE-2013-0889
CWE: CWE-264
Vulnerability description: Google Chrome before 25.0.1364.97 on Windows and Linux, and before 25.0.1364.99 on Mac OS X, does not properly enforce a user gesture requirement before proceeding with a file download, which might make it easier for remote attackers to execute arbitrary code via a crafted file.

Project: Chrome

void ChromeDownloadManagerDelegate::CheckClientDownloadDone(
    int32 download_id,
    DownloadProtectionService::DownloadCheckResult result) {
  DownloadItem* item = download_manager_->GetDownload(download_id);
  if (!item || (item->GetState() != DownloadItem::IN_PROGRESS))
    return;

  VLOG(2) << __FUNCTION__ << "() download = " << item->DebugString(false)
          << " verdict = " << result;
  if (item->GetDangerType() == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS ||
      item->GetDangerType() ==
      content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT) {
    switch (result) {
      case DownloadProtectionService::SAFE:
        break;
      case DownloadProtectionService::DANGEROUS:
        item->OnContentCheckCompleted(
            content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT);
        break;
      case DownloadProtectionService::UNCOMMON:
        item->OnContentCheckCompleted(
            content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT);
        break;
    }
  }

  SafeBrowsingState* state = static_cast<SafeBrowsingState*>(
      item->GetUserData(&safe_browsing_id));
  state->SetVerdict(result);
}

Project: Chrome

void ChromeDownloadManagerDelegate::CheckVisitedReferrerBeforeDone(
    int32 download_id,
    const content::DownloadTargetCallback& callback,
    content::DownloadDangerType danger_type,
    bool visited_referrer_before) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  DownloadItem* download =
      download_manager_->GetDownload(download_id);
  if (!download || (download->GetState() != DownloadItem::IN_PROGRESS))
    return;

  bool should_prompt = (download->GetTargetDisposition() ==
                        DownloadItem::TARGET_DISPOSITION_PROMPT);
  bool is_forced_path = !download->GetForcedFilePath().empty();
  FilePath suggested_path;

  if (!is_forced_path) {
    FilePath generated_name;
    GenerateFileNameFromRequest(
        *download,
        &generated_name,
        profile_->GetPrefs()->GetString(prefs::kDefaultCharset));

    if (download_prefs_->PromptForDownload()) {
      if (!download_crx_util::IsExtensionDownload(*download) &&
          !ShouldOpenFileBasedOnExtension(generated_name))
        should_prompt = true;
    }
    if (download_prefs_->IsDownloadPathManaged())
      should_prompt = false;

    FilePath target_directory;
    if (should_prompt && !last_download_path_.empty())
      target_directory = last_download_path_;
    else
      target_directory = download_prefs_->DownloadPath();
    suggested_path = target_directory.Append(generated_name);
  } else {
    DCHECK(!should_prompt);
    suggested_path = download->GetForcedFilePath();
  }

  if (ShouldOpenWithWebIntents(download)) {
    download->SetDisplayName(suggested_path.BaseName());
    suggested_path = suggested_path.AddExtension(kWebIntentsFileExtension);
  }

  if (danger_type == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) {
    if (!should_prompt && !is_forced_path &&
        IsDangerousFile(*download, suggested_path, visited_referrer_before)) {
      danger_type = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE;
    }

#if defined(FULL_SAFE_BROWSING)
    DownloadProtectionService* service = GetDownloadProtectionService();
    if (service && service->enabled()) {
      DownloadProtectionService::DownloadInfo info =
          DownloadProtectionService::DownloadInfo::FromDownloadItem(*download);
      info.target_file = suggested_path;
      if (service->IsSupportedDownload(info))
        danger_type = content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT;
    }
#endif
  } else {
    DCHECK_EQ(content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL, danger_type);
  }

#if defined (OS_CHROMEOS)
  drive::DriveDownloadObserver::SubstituteDriveDownloadPath(
      profile_, suggested_path, download,
      base::Bind(
          &ChromeDownloadManagerDelegate::SubstituteDriveDownloadPathCallback,
          this, download->GetId(), callback, should_prompt, is_forced_path,
          danger_type));
#else
  GetReservedPath(
      *download, suggested_path, download_prefs_->DownloadPath(),
      !is_forced_path,
      base::Bind(&ChromeDownloadManagerDelegate::OnPathReservationAvailable,
                 this, download->GetId(), callback, should_prompt,
                 danger_type));
#endif
}

Project: Chrome

void ChromeDownloadManagerDelegate::ChooseDownloadPath(
    DownloadItem* item,
    const FilePath& suggested_path,
    const FileSelectedCallback& file_selected_callback) {
  DownloadFilePicker* file_picker =
#if defined(OS_CHROMEOS)
      new DownloadFilePickerChromeOS();
#else
      new DownloadFilePicker();
#endif
  file_picker->Init(download_manager_, item, suggested_path,
                    file_selected_callback);
}

Project: Chrome

ChromeDownloadManagerDelegate::ChromeDownloadManagerDelegate(Profile* profile)
    : profile_(profile),
      next_download_id_(0),
      download_prefs_(new DownloadPrefs(profile)) {
}

Project: Chrome

void ChromeDownloadManagerDelegate::ClearLastDownloadPath() {
  last_download_path_.clear();
}

Project: Chrome

void DeleteFile(const FilePath& file_path) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  file_util::Delete(file_path, false);
}

Project: Chrome

void ChromeDownloadManagerDelegate::DisableSafeBrowsing(DownloadItem* item) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
#if defined(FULL_SAFE_BROWSING)
  SafeBrowsingState* state = static_cast<SafeBrowsingState*>(
      item->GetUserData(&safe_browsing_id));
  if (!state) {
    state = new SafeBrowsingState();
    item->SetUserData(&safe_browsing_id, state);
  }
  state->SetVerdict(DownloadProtectionService::SAFE);
#endif
}

Project: Chrome

void GenerateFileNameFromRequest(const DownloadItem& download_item,
                                 FilePath* generated_name,
                                 std::string referrer_charset) {
  std::string default_file_name(
      l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));

  *generated_name = net::GenerateFileName(download_item.GetURL(),
                                          download_item.GetContentDisposition(),
                                          referrer_charset,
                                          download_item.GetSuggestedFilename(),
                                          download_item.GetMimeType(),
                                          default_file_name);
}

Project: Chrome

    GetAlternativeWebContentsToNotifyForDownload() {
#if defined(OS_ANDROID)
  return NULL;
#else
  Browser* last_active = chrome::FindLastActiveWithProfile(profile_,
      chrome::GetActiveDesktop());
  return last_active ? chrome::GetActiveWebContents(last_active) : NULL;
#endif
}

Project: Chrome

    ChromeDownloadManagerDelegate::GetDownloadProtectionService() {
#if defined(FULL_SAFE_BROWSING)
  SafeBrowsingService* sb_service = g_browser_process->safe_browsing_service();
  if (sb_service && sb_service->download_protection_service() &&
      profile_->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) {
    return sb_service->download_protection_service();
  }
#endif
  return NULL;
}

Project: Chrome

void ChromeDownloadManagerDelegate::GetReservedPath(
    DownloadItem& download,
    const FilePath& target_path,
    const FilePath& default_download_path,
    bool should_uniquify_path,
    const DownloadPathReservationTracker::ReservedPathCallback& callback) {
  DownloadPathReservationTracker::GetReservedPath(
      download, target_path, default_download_path, should_uniquify_path,
      callback);
}

Project: Chrome

void ChromeDownloadManagerDelegate::GetSaveDir(BrowserContext* browser_context,
                                               FilePath* website_save_dir,
                                               FilePath* download_save_dir,
                                               bool* skip_dir_check) {
  Profile* profile = Profile::FromBrowserContext(browser_context);
  PrefServiceSyncable* prefs = profile->GetPrefs();

  if (!prefs->FindPreference(prefs::kSaveFileDefaultDirectory)) {
    DCHECK(prefs->FindPreference(prefs::kDownloadDefaultDirectory));
    FilePath default_save_path = prefs->GetFilePath(
        prefs::kDownloadDefaultDirectory);
    prefs->RegisterFilePathPref(prefs::kSaveFileDefaultDirectory,
                                default_save_path,
                                PrefServiceSyncable::UNSYNCABLE_PREF);
  }

  *website_save_dir = prefs->GetFilePath(prefs::kSaveFileDefaultDirectory);
  DCHECK(!website_save_dir->empty());

  *download_save_dir = prefs->GetFilePath(prefs::kDownloadDefaultDirectory);

  *skip_dir_check = false;
#if defined(OS_CHROMEOS)
  *skip_dir_check = drive::util::IsUnderDriveMountPoint(*website_save_dir);
#endif
}

Project: Chrome

bool ChromeDownloadManagerDelegate::IsDownloadReadyForCompletion(
    DownloadItem* item,
    const base::Closure& internal_complete_callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
#if defined(FULL_SAFE_BROWSING)
  SafeBrowsingState* state = static_cast<SafeBrowsingState*>(
      item->GetUserData(&safe_browsing_id));
  if (!state) {
    DownloadProtectionService* service = GetDownloadProtectionService();
    if (service) {
      VLOG(2) << __FUNCTION__ << "() Start SB download check for download = "
              << item->DebugString(false);
      state = new SafeBrowsingState();
      state->set_callback(internal_complete_callback);
      item->SetUserData(&safe_browsing_id, state);
      service->CheckClientDownload(
          DownloadProtectionService::DownloadInfo::FromDownloadItem(*item),
          base::Bind(
              &ChromeDownloadManagerDelegate::CheckClientDownloadDone,
              this,
              item->GetId()));
      return false;
    }
  } else if (!state->is_complete()) {
    state->set_callback(internal_complete_callback);
    return false;
  }
#endif
  return true;
}

Project: Chrome

void ChromeDownloadManagerDelegate::OnPathReservationAvailable(
    int32 download_id,
    const content::DownloadTargetCallback& callback,
    bool should_prompt,
    content::DownloadDangerType danger_type,
    const FilePath& reserved_path,
    bool reserved_path_verified) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DownloadItem* download =
      download_manager_->GetDownload(download_id);
  if (!download || (download->GetState() != DownloadItem::IN_PROGRESS))
    return;
  if (should_prompt || !reserved_path_verified) {
    ChooseDownloadPath(
        download, reserved_path,
        base::Bind(&ChromeDownloadManagerDelegate::OnTargetPathDetermined,
                   this, download_id, callback,
                   DownloadItem::TARGET_DISPOSITION_PROMPT, danger_type));
  } else {
    OnTargetPathDetermined(download_id, callback,
                           DownloadItem::TARGET_DISPOSITION_OVERWRITE,
                           danger_type, reserved_path);
  }
}

Project: Chrome

void ChromeDownloadManagerDelegate::OnTargetPathDetermined(
    int32 download_id,
    const content::DownloadTargetCallback& callback,
    DownloadItem::TargetDisposition disposition,
    content::DownloadDangerType danger_type,
    const FilePath& target_path) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  FilePath intermediate_path;
  DownloadItem* download =
      download_manager_->GetDownload(download_id);
  if (!download || (download->GetState() != DownloadItem::IN_PROGRESS))
    return;

  if (!target_path.empty()) {
    intermediate_path = GetIntermediatePath(target_path, danger_type);

    if (disposition == DownloadItem::TARGET_DISPOSITION_PROMPT &&
        !download->IsTemporary())
      last_download_path_ = target_path.DirName();
  }
  callback.Run(target_path, disposition, danger_type, intermediate_path);
}

Project: Chrome

void ChromeDownloadManagerDelegate::OpenDownload(DownloadItem* download) {
  platform_util::OpenItem(download->GetFullPath());
}

Project: Chrome

void ChromeDownloadManagerDelegate::OpenWithWebIntent(
    const DownloadItem* item) {
  webkit_glue::WebIntentData intent_data(
      ASCIIToUTF16("http://webintents.org/view"),
      ASCIIToUTF16(item->GetMimeType()),
      item->GetFullPath(),
      item->GetReceivedBytes());

  intent_data.extra_data.insert(make_pair(
      ASCIIToUTF16("url"), ASCIIToUTF16(item->GetURL().spec())));

  intent_data.extra_data.insert(
      make_pair(ASCIIToUTF16("filename"),
                item->GetFileNameToReportUser().LossyDisplayName()));

  content::WebIntentsDispatcher* dispatcher =
      content::WebIntentsDispatcher::Create(intent_data);
  dispatcher->RegisterReplyNotification(
      base::Bind(&OnWebIntentDispatchCompleted, item->GetFullPath()));

  content::WebContentsDelegate* delegate = NULL;
#if !defined(OS_ANDROID)
  if (item->GetWebContents() && item->GetWebContents()->GetDelegate()) {
    delegate = item->GetWebContents()->GetDelegate();
  } else {
    delegate = web_intents::GetBrowserForBackgroundWebIntentDelivery(profile_);
  }
#endif  // !defined(OS_ANDROID)
  DCHECK(delegate);
  delegate->WebIntentDispatch(NULL, dispatcher);
}

Project: Chrome

void ChromeDownloadManagerDelegate::SetDownloadManager(DownloadManager* dm) {
  download_manager_ = dm;
#if !defined(OS_ANDROID)
  extension_event_router_.reset(new ExtensionDownloadsEventRouter(
      profile_, download_manager_));
#endif
}

Project: Chrome

bool ChromeDownloadManagerDelegate::ShouldCompleteDownload(
    DownloadItem* item,
    const base::Closure& user_complete_callback) {
  return IsDownloadReadyForCompletion(item, base::Bind(
      &ChromeDownloadManagerDelegate::ShouldCompleteDownloadInternal,
      this, item->GetId(), user_complete_callback));
}

Project: Chrome

void ChromeDownloadManagerDelegate::ShouldCompleteDownloadInternal(
    int download_id,
    const base::Closure& user_complete_callback) {
  DownloadItem* item = download_manager_->GetDownload(download_id);
  if (!item)
    return;
  if (ShouldCompleteDownload(item, user_complete_callback))
    user_complete_callback.Run();
}

Project: Chrome

bool ChromeDownloadManagerDelegate::ShouldOpenDownload(
    DownloadItem* item, const content::DownloadOpenDelayedCallback& callback) {
  if (download_crx_util::IsExtensionDownload(*item)) {
    scoped_refptr<extensions::CrxInstaller> crx_installer =
        download_crx_util::OpenChromeExtension(profile_, *item);

    registrar_.Add(
        this,
        chrome::NOTIFICATION_CRX_INSTALLER_DONE,
        content::Source<extensions::CrxInstaller>(crx_installer.get()));

    crx_installers_[crx_installer.get()] = callback;
    item->UpdateObservers();
    return false;
  }

  if (ShouldOpenWithWebIntents(item)) {
    OpenWithWebIntent(item);
    callback.Run(true);
    return false;
  }

  return true;
}

Project: Chrome

bool ChromeDownloadManagerDelegate::ShouldOpenFileBasedOnExtension(
    const FilePath& path) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  FilePath::StringType extension = path.Extension();
  if (extension.empty())
    return false;
  if (extensions::Extension::IsExtension(path))
    return false;
  DCHECK(extension[0] == FilePath::kExtensionSeparator);
  extension.erase(0, 1);
  return download_prefs_->IsAutoOpenEnabledForExtension(extension);
}

Project: Chrome

bool ChromeDownloadManagerDelegate::ShouldOpenWithWebIntents(
    const DownloadItem* item) {
  if (!web_intents::IsWebIntentsEnabledForProfile(profile_))
    return false;

  if ((item->GetWebContents() && !item->GetWebContents()->GetDelegate()) &&
      !web_intents::GetBrowserForBackgroundWebIntentDelivery(profile_)) {
    return false;
  }
  if (!item->GetForcedFilePath().empty())
    return false;
  if (item->GetTargetDisposition() == DownloadItem::TARGET_DISPOSITION_PROMPT)
    return false;

#if !defined(OS_CHROMEOS)
  std::string mime_type = item->GetMimeType();

  const char kQuickOfficeExtensionId[] = "gbkeegbaiigmenfmjfclcdgdpimamgkj";
  const char kQuickOfficeDevExtensionId[] = "ionpfmkccalenbmnddpbmocokhaknphg";
  ExtensionServiceInterface* extension_service =
      extensions::ExtensionSystem::Get(profile_)->extension_service();

  bool use_quickoffice = false;
  if (extension_service &&
      extension_service->GetInstalledExtension(kQuickOfficeExtensionId) &&
      extension_service->IsExtensionEnabled(kQuickOfficeExtensionId))
    use_quickoffice = true;

  if (extension_service &&
      extension_service->GetInstalledExtension(kQuickOfficeDevExtensionId) &&
      extension_service->IsExtensionEnabled(kQuickOfficeDevExtensionId))
    use_quickoffice = true;

  if (use_quickoffice) {
    if (mime_type == "application/msword" ||
        mime_type == "application/vnd.ms-powerpoint" ||
        mime_type == "application/vnd.ms-excel" ||
        mime_type == "application/vnd.openxmlformats-officedocument."
                     "wordprocessingml.document" ||
        mime_type == "application/vnd.openxmlformats-officedocument."
                     "presentationml.presentation" ||
        mime_type == "application/vnd.openxmlformats-officedocument."
                     "spreadsheetml.sheet") {
      return true;
    }
  }
#endif  // !defined(OS_CHROMEOS)

  return false;
}

Project: Chrome

void ChromeDownloadManagerDelegate::ShowDownloadInShell(
    DownloadItem* download) {
  platform_util::ShowItemInFolder(download->GetFullPath());
}

Project: Chrome

void ChromeDownloadManagerDelegate::Shutdown() {
  download_prefs_.reset();
#if !defined(OS_ANDROID)
  extension_event_router_.reset();
#endif
}

Project: Chrome

void ChromeDownloadManagerDelegate::SubstituteDriveDownloadPathCallback(
    int32 download_id,
    const content::DownloadTargetCallback& callback,
    bool should_prompt,
    bool is_forced_path,
    content::DownloadDangerType danger_type,
    const FilePath& suggested_path) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DownloadItem* download =
      download_manager_->GetDownload(download_id);
  if (!download || (download->GetState() != DownloadItem::IN_PROGRESS))
    return;

  if (suggested_path.empty()) {
    callback.Run(FilePath(),
                 DownloadItem::TARGET_DISPOSITION_OVERWRITE,
                 danger_type,
                 FilePath());
    return;
  }

  GetReservedPath(
      *download, suggested_path, download_prefs_->DownloadPath(),
      !is_forced_path,
      base::Bind(&ChromeDownloadManagerDelegate::OnPathReservationAvailable,
                 this, download->GetId(), callback, should_prompt,
                 danger_type));
}

Project: Chrome

void VisitCountsToVisitedBefore(
    const VisitedBeforeCallback& callback,
    HistoryService::Handle unused_handle,
    bool found_visits,
    int count,
    base::Time first_visit) {
  callback.Run(found_visits && count &&
      (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight()));
}