| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/download/quarantine.h" | 5 #include "content/browser/download/quarantine.h" |
| 6 | 6 |
| 7 #include <ApplicationServices/ApplicationServices.h> | 7 #import <ApplicationServices/ApplicationServices.h> |
| 8 #include <Foundation/Foundation.h> | 8 #import <Foundation/Foundation.h> |
| 9 | 9 |
| 10 #include "base/files/file_path.h" | 10 #include "base/files/file_path.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/mac/foundation_util.h" | 12 #include "base/mac/foundation_util.h" |
| 13 #include "base/mac/mac_logging.h" | 13 #include "base/mac/mac_logging.h" |
| 14 #include "base/mac/mac_util.h" | 14 #include "base/mac/mac_util.h" |
| 15 #include "base/mac/scoped_cftyperef.h" | 15 #include "base/mac/scoped_cftyperef.h" |
| 16 #include "base/mac/scoped_nsobject.h" |
| 17 #include "base/strings/sys_string_conversions.h" |
| 16 #include "base/threading/thread_restrictions.h" | 18 #include "base/threading/thread_restrictions.h" |
| 17 #include "url/gurl.h" | 19 #include "url/gurl.h" |
| 18 | 20 |
| 21 namespace { |
| 22 |
| 23 // Once Chrome no longer supports macOS 10.9, this code will no longer be |
| 24 // necessary. Note that LSCopyItemAttribute was deprecated in macOS 10.8, but |
| 25 // the replacement to kLSItemQuarantineProperties did not exist until macOS |
| 26 // 10.10. |
| 27 #if !defined(MAC_OS_X_VERSION_10_10) || \ |
| 28 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 |
| 29 #pragma clang diagnostic push |
| 30 #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| 31 bool GetQuarantinePropertiesDeprecated( |
| 32 const base::FilePath& file, |
| 33 base::scoped_nsobject<NSMutableDictionary>* properties) { |
| 34 FSRef file_ref; |
| 35 if (!base::mac::FSRefFromPath(file.value(), &file_ref)) |
| 36 return false; |
| 37 |
| 38 base::ScopedCFTypeRef<CFTypeRef> quarantine_properties; |
| 39 OSStatus status = LSCopyItemAttribute(&file_ref, kLSRolesAll, |
| 40 kLSItemQuarantineProperties, quarantine_properties.InitializeInto()); |
| 41 if (status != noErr) |
| 42 return true; |
| 43 |
| 44 CFDictionaryRef quarantine_properties_dict = |
| 45 base::mac::CFCast<CFDictionaryRef>(quarantine_properties.get()); |
| 46 if (!quarantine_properties_dict) { |
| 47 LOG(WARNING) << "kLSItemQuarantineProperties is not a dictionary on file " |
| 48 << file.value(); |
| 49 return false; |
| 50 } |
| 51 |
| 52 properties->reset( |
| 53 [base::mac::CFToNSCast(quarantine_properties_dict) mutableCopy]); |
| 54 return true; |
| 55 } |
| 56 |
| 57 bool SetQuarantinePropertiesDeprecated(const base::FilePath& file, |
| 58 NSDictionary* properties) { |
| 59 FSRef file_ref; |
| 60 if (!base::mac::FSRefFromPath(file.value(), &file_ref)) |
| 61 return false; |
| 62 OSStatus os_error = LSSetItemAttribute( |
| 63 &file_ref, kLSRolesAll, kLSItemQuarantineProperties, properties); |
| 64 if (os_error != noErr) { |
| 65 OSSTATUS_LOG(WARNING, os_error) |
| 66 << "Unable to set quarantine attributes on file " << file.value(); |
| 67 return false; |
| 68 } |
| 69 return true; |
| 70 } |
| 71 #pragma clang diagnostic pop |
| 72 #endif |
| 73 |
| 74 bool GetQuarantineProperties( |
| 75 const base::FilePath& file, |
| 76 base::scoped_nsobject<NSMutableDictionary>* properties) { |
| 77 base::scoped_nsobject<NSURL> file_url([[NSURL alloc] |
| 78 initFileURLWithPath:base::SysUTF8ToNSString(file.value())]); |
| 79 if (!file_url) |
| 80 return false; |
| 81 |
| 82 // NSURLQuarantinePropertiesKey is only available on macOS 10.10+. |
| 83 #pragma clang diagnostic push |
| 84 #pragma clang diagnostic ignored "-Wunguarded-availability" |
| 85 NSError* error = nil; |
| 86 id quarantine_properties = nil; |
| 87 BOOL success = [file_url getResourceValue:&quarantine_properties |
| 88 forKey:NSURLQuarantinePropertiesKey |
| 89 error:&error]; |
| 90 #pragma clang diagnostic pop |
| 91 if (!success) { |
| 92 std::string error_message(error ? error.description.UTF8String : ""); |
| 93 LOG(WARNING) << "Unable to get quarantine attributes for file " |
| 94 << file.value() << ". Error: " << error_message; |
| 95 return false; |
| 96 } |
| 97 |
| 98 if (!quarantine_properties) |
| 99 return true; |
| 100 |
| 101 NSDictionary* quarantine_properties_dict = |
| 102 base::mac::ObjCCast<NSDictionary>(quarantine_properties); |
| 103 if (!quarantine_properties_dict) { |
| 104 LOG(WARNING) << "Quarantine properties have wrong class: " |
| 105 << [[[quarantine_properties class] description] UTF8String]; |
| 106 return false; |
| 107 } |
| 108 |
| 109 properties->reset([quarantine_properties_dict mutableCopy]); |
| 110 return true; |
| 111 } |
| 112 |
| 113 bool SetQuarantineProperties(const base::FilePath& file, |
| 114 NSDictionary* properties) { |
| 115 base::scoped_nsobject<NSURL> file_url([[NSURL alloc] |
| 116 initFileURLWithPath:base::SysUTF8ToNSString(file.value())]); |
| 117 if (!file_url) |
| 118 return false; |
| 119 |
| 120 // NSURLQuarantinePropertiesKey is only available on macOS 10.10+. |
| 121 #pragma clang diagnostic push |
| 122 #pragma clang diagnostic ignored "-Wunguarded-availability" |
| 123 NSError* error = nil; |
| 124 bool success = [file_url setResourceValue:properties |
| 125 forKey:NSURLQuarantinePropertiesKey |
| 126 error:&error]; |
| 127 #pragma clang diagnostic pop |
| 128 if (!success) { |
| 129 std::string error_message(error ? error.description.UTF8String : ""); |
| 130 LOG(WARNING) << "Unable to set quarantine attributes on file " |
| 131 << file.value() << ". Error: " << error_message; |
| 132 return false; |
| 133 } |
| 134 return true; |
| 135 } |
| 136 |
| 137 } // namespace |
| 138 |
| 19 namespace content { | 139 namespace content { |
| 20 | 140 |
| 21 namespace { | 141 namespace { |
| 22 | 142 |
| 23 // As of Mac OS X 10.4 ("Tiger"), files can be tagged with metadata describing | 143 // As of Mac OS X 10.4 ("Tiger"), files can be tagged with metadata describing |
| 24 // various attributes. Metadata is integrated with the system's Spotlight | 144 // various attributes. Metadata is integrated with the system's Spotlight |
| 25 // feature and is searchable. Ordinarily, metadata can only be set by | 145 // feature and is searchable. Ordinarily, metadata can only be set by |
| 26 // Spotlight importers, which requires that the importer own the target file. | 146 // Spotlight importers, which requires that the importer own the target file. |
| 27 // However, there's an attribute intended to describe the origin of a | 147 // However, there's an attribute intended to describe the origin of a |
| 28 // file, that can store the source URL and referrer of a downloaded file. | 148 // file, that can store the source URL and referrer of a downloaded file. |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 104 // the URL the user initiated the download from. | 224 // the URL the user initiated the download from. |
| 105 | 225 |
| 106 // The OS will automatically quarantine files due to the | 226 // The OS will automatically quarantine files due to the |
| 107 // LSFileQuarantineEnabled entry in our Info.plist, but it knows relatively | 227 // LSFileQuarantineEnabled entry in our Info.plist, but it knows relatively |
| 108 // little about the files. We add more information about the download to | 228 // little about the files. We add more information about the download to |
| 109 // improve the UI shown by the OS when the users tries to open the file. | 229 // improve the UI shown by the OS when the users tries to open the file. |
| 110 bool AddQuarantineMetadataToFile(const base::FilePath& file, | 230 bool AddQuarantineMetadataToFile(const base::FilePath& file, |
| 111 const GURL& source, | 231 const GURL& source, |
| 112 const GURL& referrer) { | 232 const GURL& referrer) { |
| 113 base::ThreadRestrictions::AssertIOAllowed(); | 233 base::ThreadRestrictions::AssertIOAllowed(); |
| 114 FSRef file_ref; | 234 base::scoped_nsobject<NSMutableDictionary> properties; |
| 115 if (!base::mac::FSRefFromPath(file.value(), &file_ref)) | 235 bool success = false; |
| 236 if (base::mac::IsAtLeastOS10_10()) { |
| 237 success = GetQuarantineProperties(file, &properties); |
| 238 } else { |
| 239 success = GetQuarantinePropertiesDeprecated(file, &properties); |
| 240 } |
| 241 |
| 242 if (!success) |
| 116 return false; | 243 return false; |
| 117 | 244 |
| 118 NSMutableDictionary* quarantine_properties = nil; | 245 if (!properties) { |
| 119 CFTypeRef quarantine_properties_base = NULL; | |
| 120 if (LSCopyItemAttribute(&file_ref, kLSRolesAll, kLSItemQuarantineProperties, | |
| 121 &quarantine_properties_base) == noErr) { | |
| 122 if (CFGetTypeID(quarantine_properties_base) == CFDictionaryGetTypeID()) { | |
| 123 // Quarantine properties will already exist if LSFileQuarantineEnabled | |
| 124 // is on and the file doesn't match an exclusion. | |
| 125 quarantine_properties = | |
| 126 [[(NSDictionary*)quarantine_properties_base mutableCopy] autorelease]; | |
| 127 } else { | |
| 128 LOG(WARNING) << "kLSItemQuarantineProperties is not a dictionary on file " | |
| 129 << file.value(); | |
| 130 } | |
| 131 CFRelease(quarantine_properties_base); | |
| 132 } | |
| 133 | |
| 134 if (!quarantine_properties) { | |
| 135 // If there are no quarantine properties, then the file isn't quarantined | 246 // If there are no quarantine properties, then the file isn't quarantined |
| 136 // (e.g., because the user has set up exclusions for certain file types). | 247 // (e.g., because the user has set up exclusions for certain file types). |
| 137 // We don't want to add any metadata, because that will cause the file to | 248 // We don't want to add any metadata, because that will cause the file to |
| 138 // be quarantined against the user's wishes. | 249 // be quarantined against the user's wishes. |
| 139 return true; | 250 return true; |
| 140 } | 251 } |
| 141 | 252 |
| 142 // kLSQuarantineAgentNameKey, kLSQuarantineAgentBundleIdentifierKey, and | 253 // kLSQuarantineAgentNameKey, kLSQuarantineAgentBundleIdentifierKey, and |
| 143 // kLSQuarantineTimeStampKey are set for us (see LSQuarantine.h), so we only | 254 // kLSQuarantineTimeStampKey are set for us (see LSQuarantine.h), so we only |
| 144 // need to set the values that the OS can't infer. | 255 // need to set the values that the OS can't infer. |
| 145 | 256 |
| 146 if (![quarantine_properties valueForKey:(NSString*)kLSQuarantineTypeKey]) { | 257 if (![properties valueForKey:(NSString*)kLSQuarantineTypeKey]) { |
| 147 CFStringRef type = source.SchemeIsHTTPOrHTTPS() | 258 CFStringRef type = source.SchemeIsHTTPOrHTTPS() |
| 148 ? kLSQuarantineTypeWebDownload | 259 ? kLSQuarantineTypeWebDownload |
| 149 : kLSQuarantineTypeOtherDownload; | 260 : kLSQuarantineTypeOtherDownload; |
| 150 [quarantine_properties setValue:(NSString*)type | 261 [properties setValue:(NSString*)type |
| 151 forKey:(NSString*)kLSQuarantineTypeKey]; | 262 forKey:(NSString*)kLSQuarantineTypeKey]; |
| 152 } | 263 } |
| 153 | 264 |
| 154 if (![quarantine_properties | 265 if (![properties valueForKey:(NSString*)kLSQuarantineOriginURLKey] && |
| 155 valueForKey:(NSString*)kLSQuarantineOriginURLKey] && | |
| 156 referrer.is_valid()) { | 266 referrer.is_valid()) { |
| 157 NSString* referrer_url = | 267 NSString* referrer_url = |
| 158 [NSString stringWithUTF8String:referrer.spec().c_str()]; | 268 [NSString stringWithUTF8String:referrer.spec().c_str()]; |
| 159 [quarantine_properties setValue:referrer_url | 269 [properties setValue:referrer_url |
| 160 forKey:(NSString*)kLSQuarantineOriginURLKey]; | 270 forKey:(NSString*)kLSQuarantineOriginURLKey]; |
| 161 } | 271 } |
| 162 | 272 |
| 163 if (![quarantine_properties valueForKey:(NSString*)kLSQuarantineDataURLKey] && | 273 if (![properties valueForKey:(NSString*)kLSQuarantineDataURLKey] && |
| 164 source.is_valid()) { | 274 source.is_valid()) { |
| 165 NSString* origin_url = | 275 NSString* origin_url = |
| 166 [NSString stringWithUTF8String:source.spec().c_str()]; | 276 [NSString stringWithUTF8String:source.spec().c_str()]; |
| 167 [quarantine_properties setValue:origin_url | 277 [properties setValue:origin_url forKey:(NSString*)kLSQuarantineDataURLKey]; |
| 168 forKey:(NSString*)kLSQuarantineDataURLKey]; | |
| 169 } | 278 } |
| 170 | 279 |
| 171 OSStatus os_error = | 280 if (base::mac::IsAtLeastOS10_10()) { |
| 172 LSSetItemAttribute(&file_ref, kLSRolesAll, kLSItemQuarantineProperties, | 281 return SetQuarantineProperties(file, properties); |
| 173 quarantine_properties); | 282 } else { |
| 174 if (os_error != noErr) { | 283 return SetQuarantinePropertiesDeprecated(file, properties); |
| 175 OSSTATUS_LOG(WARNING, os_error) | |
| 176 << "Unable to set quarantine attributes on file " << file.value(); | |
| 177 return false; | |
| 178 } | 284 } |
| 179 return true; | |
| 180 } | 285 } |
| 181 | 286 |
| 182 } // namespace | 287 } // namespace |
| 183 | 288 |
| 184 QuarantineFileResult QuarantineFile(const base::FilePath& file, | 289 QuarantineFileResult QuarantineFile(const base::FilePath& file, |
| 185 const GURL& source_url, | 290 const GURL& source_url, |
| 186 const GURL& referrer_url, | 291 const GURL& referrer_url, |
| 187 const std::string& client_guid) { | 292 const std::string& client_guid) { |
| 188 bool quarantine_succeeded = | 293 bool quarantine_succeeded = |
| 189 AddQuarantineMetadataToFile(file, source_url, referrer_url); | 294 AddQuarantineMetadataToFile(file, source_url, referrer_url); |
| 190 bool origin_succeeded = | 295 bool origin_succeeded = |
| 191 AddOriginMetadataToFile(file, source_url, referrer_url); | 296 AddOriginMetadataToFile(file, source_url, referrer_url); |
| 192 return quarantine_succeeded && origin_succeeded | 297 return quarantine_succeeded && origin_succeeded |
| 193 ? QuarantineFileResult::OK | 298 ? QuarantineFileResult::OK |
| 194 : QuarantineFileResult::ANNOTATION_FAILED; | 299 : QuarantineFileResult::ANNOTATION_FAILED; |
| 195 } | 300 } |
| 196 | 301 |
| 197 } // namespace content | 302 } // namespace content |
| OLD | NEW |