Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1306)

Side by Side Diff: chrome/common/mac/cfbundle_blocker.mm

Issue 8226026: Allow whitelisted bundles to load in Chrome (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 "chrome/common/mac/cfbundle_blocker.h" 5 #include "chrome/common/mac/cfbundle_blocker.h"
6 6
7 #include <CoreFoundation/CoreFoundation.h> 7 #include <CoreFoundation/CoreFoundation.h>
8 #import <Foundation/Foundation.h> 8 #import <Foundation/Foundation.h>
9 9
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/mac/mac_util.h" 11 #include "base/mac/mac_util.h"
12 #include "base/mac/scoped_cftyperef.h" 12 #include "base/mac/scoped_cftyperef.h"
13 #include "base/mac/scoped_nsautorelease_pool.h" 13 #include "base/mac/scoped_nsautorelease_pool.h"
14 #import "base/memory/scoped_nsobject.h"
14 #include "base/sys_string_conversions.h" 15 #include "base/sys_string_conversions.h"
15 #include "third_party/mach_override/mach_override.h" 16 #include "third_party/mach_override/mach_override.h"
16 17
17 extern "C" { 18 extern "C" {
18 19
19 // _CFBundleLoadExecutableAndReturnError is the internal implementation that 20 // _CFBundleLoadExecutableAndReturnError is the internal implementation that
20 // results in a dylib being loaded via dlopen. Both CFBundleLoadExecutable and 21 // results in a dylib being loaded via dlopen. Both CFBundleLoadExecutable and
21 // CFBundleLoadExecutableAndReturnError are funneled into this routine. Other 22 // CFBundleLoadExecutableAndReturnError are funneled into this routine. Other
22 // CFBundle functions may also call directly into here, perhaps due to 23 // CFBundle functions may also call directly into here, perhaps due to
23 // inlining their calls to CFBundleLoadExecutable. 24 // inlining their calls to CFBundleLoadExecutable.
(...skipping 29 matching lines...) Expand all
53 NSArray* blocked_prefixes = 54 NSArray* blocked_prefixes =
54 NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, 55 NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
55 NSUserDomainMask | 56 NSUserDomainMask |
56 NSLocalDomainMask | 57 NSLocalDomainMask |
57 NSNetworkDomainMask, 58 NSNetworkDomainMask,
58 YES); 59 YES);
59 60
60 // Everything in the suffix list has a trailing slash so as to only block 61 // Everything in the suffix list has a trailing slash so as to only block
61 // loading things contained in these directories. 62 // loading things contained in these directories.
62 NSString* const blocked_suffixes[] = { 63 NSString* const blocked_suffixes[] = {
63 // SIMBL - http://code.google.com/p/simbl/source/browse/src/SIMBL.{h,m}.
64 // It attempts to inject itself via an AppleScript event.
65 // http://code.google.com/p/simbl/source/browse/SIMBL%20Agent/SIMBLAgent.m
66 // Blocking input managers and scripting additions may already be enough
67 // to prevent SIMBL plugins from loading.
68 @"Application Support/SIMBL/Plugins/",
69
70 #if !defined(__LP64__) 64 #if !defined(__LP64__)
71 // Contextual menu manager plug-ins are unavailable to 64-bit processes. 65 // Contextual menu manager plug-ins are unavailable to 64-bit processes.
72 // http://developer.apple.com/library/mac/releasenotes/Cocoa/AppKitOlderNo tes.html#NSMenu 66 // http://developer.apple.com/library/mac/releasenotes/Cocoa/AppKitOlderNo tes.html#NSMenu
73 // Contextual menu plug-ins are loaded when a contextual menu is opened, 67 // Contextual menu plug-ins are loaded when a contextual menu is opened,
74 // for example, from within 68 // for example, from within
75 // +[NSMenu popUpContextMenu:withEvent:forView:]. 69 // +[NSMenu popUpContextMenu:withEvent:forView:].
76 @"Contextual Menu Items/", 70 @"Contextual Menu Items/",
77 71
78 // Input managers are deprecated, would only be loaded under specific 72 // Input managers are deprecated, would only be loaded under specific
79 // circumstances, and are entirely unavailable to 64-bit processes. 73 // circumstances, and are entirely unavailable to 64-bit processes.
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
139 // If bundle_path is inside blocked_path (it has blocked_path as a 133 // If bundle_path is inside blocked_path (it has blocked_path as a
140 // prefix), refuse to load it. 134 // prefix), refuse to load it.
141 return true; 135 return true;
142 } 136 }
143 } 137 }
144 138
145 // bundle_path is not inside any blocked_path from blocked_paths. 139 // bundle_path is not inside any blocked_path from blocked_paths.
146 return false; 140 return false;
147 } 141 }
148 142
149 // Returns true if bundle_id identifies a bundle that is allowed to be loaded
150 // even when found in a blocked directory.
151 bool IsBundleIDAllowed(NSString* bundle_id) {
152 return [bundle_id isEqualToString:@"com.google.osax.Google_Authenticator_BT"];
153 }
154
155 typedef Boolean (*_CFBundleLoadExecutableAndReturnError_Type)(CFBundleRef, 143 typedef Boolean (*_CFBundleLoadExecutableAndReturnError_Type)(CFBundleRef,
156 Boolean, 144 Boolean,
157 CFErrorRef*); 145 CFErrorRef*);
158 146
159 // Call this to execute the original implementation of 147 // Call this to execute the original implementation of
160 // _CFBundleLoadExecutableAndReturnError. 148 // _CFBundleLoadExecutableAndReturnError.
161 _CFBundleLoadExecutableAndReturnError_Type 149 _CFBundleLoadExecutableAndReturnError_Type
162 g_original_underscore_cfbundle_load_executable_and_return_error; 150 g_original_underscore_cfbundle_load_executable_and_return_error;
163 151
164 Boolean ChromeCFBundleLoadExecutableAndReturnError(CFBundleRef bundle, 152 Boolean ChromeCFBundleLoadExecutableAndReturnError(CFBundleRef bundle,
165 Boolean force_global, 153 Boolean force_global,
166 CFErrorRef* error) { 154 CFErrorRef* error) {
167 base::mac::ScopedNSAutoreleasePool autorelease_pool; 155 base::mac::ScopedNSAutoreleasePool autorelease_pool;
168 156
169 DCHECK(g_original_underscore_cfbundle_load_executable_and_return_error); 157 DCHECK(g_original_underscore_cfbundle_load_executable_and_return_error);
170 158
171 base::mac::ScopedCFTypeRef<CFURLRef> url_cf(CFBundleCopyBundleURL(bundle)); 159 base::mac::ScopedCFTypeRef<CFURLRef> url_cf(CFBundleCopyBundleURL(bundle));
172 base::mac::ScopedCFTypeRef<CFStringRef> path_cf( 160 scoped_nsobject<NSString> path(base::mac::CFToNSCast(
173 CFURLCopyFileSystemPath(url_cf, kCFURLPOSIXPathStyle)); 161 CFURLCopyFileSystemPath(url_cf, kCFURLPOSIXPathStyle)));
174 NSString* path_ns = base::mac::CFToNSCast(path_cf);
175 162
176 CFStringRef identifier_cf = CFBundleGetIdentifier(bundle); 163 NSString* bundle_id = base::mac::CFToNSCast(CFBundleGetIdentifier(bundle));
177 NSString* identifier_ns = base::mac::CFToNSCast(identifier_cf);
178 164
179 if (IsBundlePathBlocked(path_ns) && !IsBundleIDAllowed(identifier_ns)) { 165 NSDictionary* bundle_dictionary =
180 NSString* identifier_ns_print = identifier_ns ? identifier_ns : @"(nil)"; 166 base::mac::CFToNSCast(CFBundleGetInfoDictionary(bundle));
167 NSString* version = [bundle_dictionary objectForKey:
168 base::mac::CFToNSCast(kCFBundleVersionKey)];
169 if (![version isKindOfClass:[NSString class]]) {
170 // Deal with pranksters.
171 version = nil;
172 }
173
174 if (IsBundlePathBlocked(path) && !IsBundleAllowed(bundle_id, version)) {
175 NSString* bundle_id_print = bundle_id ? bundle_id : @"(nil)";
176 NSString* version_print = version ? version : @"(nil)";
181 177
182 LOG(INFO) << "Blocking attempt to load bundle " 178 LOG(INFO) << "Blocking attempt to load bundle "
183 << [identifier_ns_print UTF8String] 179 << [bundle_id_print UTF8String]
180 << " version "
181 << [version_print UTF8String]
184 << " at " 182 << " at "
185 << [path_ns fileSystemRepresentation]; 183 << [path fileSystemRepresentation];
Scott Hess - ex-Googler 2011/10/13 00:05:54 Wonder if this would be cleaner using -[NSString s
Mark Mentovai 2011/10/13 03:23:15 shess wrote:
186 184
187 if (error) { 185 if (error) {
188 base::mac::ScopedCFTypeRef<CFStringRef> bundle_id( 186 base::mac::ScopedCFTypeRef<CFStringRef> app_bundle_id(
189 base::SysUTF8ToCFStringRef(base::mac::BaseBundleID())); 187 base::SysUTF8ToCFStringRef(base::mac::BaseBundleID()));
190 188
191 // 0xb10c10ad = "block load" 189 // 0xb10c10ad = "block load"
192 const CFIndex kBundleLoadBlocked = 0xb10c10ad; 190 const CFIndex kBundleLoadBlocked = 0xb10c10ad;
193 191
194 NSMutableDictionary* error_dict = 192 NSMutableDictionary* error_dict =
195 [NSMutableDictionary dictionaryWithCapacity:3]; 193 [NSMutableDictionary dictionaryWithCapacity:4];
196 if (identifier_ns) { 194 if (bundle_id) {
197 [error_dict setObject:identifier_ns forKey:@"identifier"]; 195 [error_dict setObject:bundle_id forKey:@"bundle_id"];
198 } 196 }
199 if (path_ns) { 197 if (version) {
200 [error_dict setObject:path_ns forKey:@"path"]; 198 [error_dict setObject:version forKey:@"version"];
199 }
200 if (path) {
201 [error_dict setObject:path forKey:@"path"];
201 } 202 }
202 NSURL* url_ns = base::mac::CFToNSCast(url_cf); 203 NSURL* url_ns = base::mac::CFToNSCast(url_cf);
203 NSString* url_absolute_string_ns = [url_ns absoluteString]; 204 NSString* url_absolute_string = [url_ns absoluteString];
204 if (url_absolute_string_ns) { 205 if (url_absolute_string) {
205 [error_dict setObject:url_absolute_string_ns forKey:@"url"]; 206 [error_dict setObject:url_absolute_string forKey:@"url"];
206 } 207 }
207 208
208 *error = CFErrorCreate(NULL, 209 *error = CFErrorCreate(NULL,
209 bundle_id, 210 app_bundle_id,
210 kBundleLoadBlocked, 211 kBundleLoadBlocked,
211 base::mac::NSToCFCast(error_dict)); 212 base::mac::NSToCFCast(error_dict));
212 } 213 }
213 214
214 return FALSE; 215 return FALSE;
215 } 216 }
216 217
217 // Not blocked. Call through to the original implementation. 218 // Not blocked. Call through to the original implementation.
218 return g_original_underscore_cfbundle_load_executable_and_return_error( 219 return g_original_underscore_cfbundle_load_executable_and_return_error(
219 bundle, force_global, error); 220 bundle, force_global, error);
220 } 221 }
221 222
222 } // namespace 223 } // namespace
223 224
224 void EnableCFBundleBlocker() { 225 void EnableCFBundleBlocker() {
225 mach_error_t err = mach_override_ptr( 226 mach_error_t err = mach_override_ptr(
226 reinterpret_cast<void*>(_CFBundleLoadExecutableAndReturnError), 227 reinterpret_cast<void*>(_CFBundleLoadExecutableAndReturnError),
227 reinterpret_cast<void*>(ChromeCFBundleLoadExecutableAndReturnError), 228 reinterpret_cast<void*>(ChromeCFBundleLoadExecutableAndReturnError),
228 reinterpret_cast<void**>( 229 reinterpret_cast<void**>(
229 &g_original_underscore_cfbundle_load_executable_and_return_error)); 230 &g_original_underscore_cfbundle_load_executable_and_return_error));
230 if (err != err_none) { 231 if (err != err_none) {
231 LOG(WARNING) << "mach_override _CFBundleLoadExecutableAndReturnError: " 232 LOG(WARNING) << "mach_override _CFBundleLoadExecutableAndReturnError: "
232 << err; 233 << err;
233 } 234 }
234 } 235 }
235 236
237 namespace {
238
239 struct AllowedBundle {
240 // The bundle identifier to permit. These are matched with a case-sensitive
241 // literal comparison. "Children" of the declared bundle ID are permitted:
242 // if bundle_id here is @"org.chromium", it would match both @"org.chromium"
243 // and @"org.chromium.Chromium".
244 NSString* bundle_id;
245
246 // If bundle_id should only be permitted as of a certain minimum version,
247 // this string defines that version, which will be compared to the bundle's
248 // version with a numeric comparison. If bundle_id may be permitted at any
249 // version, set minimum_version to nil.
250 NSString* minimum_version;
251 };
252
253 } // namespace
254
255 bool IsBundleAllowed(NSString* bundle_id, NSString* version) {
256 // The list of bundles that are allowed to load. Before adding an entry to
257 // this list, be sure that it's well-behaved. Specifically, anything that
258 // uses mach_override
259 // (https://github.com/rentzsch/mach_star/tree/master/mach_override) must
260 // use version 51ae3d199463fa84548f466d649f0821d579fdaf (July 22, 2011) or
261 // newer. Products added to the list must not cause crashes. Entries should
262 // include the name of the product, URL, and the name and e-mail address of
263 // someone responsible for the product's engineering. To add items to this
264 // list, file a bug at http://new.crbug.com/ using the "Defect on Mac OS"
265 // template, and provide the bundle ID (or IDs) and minimum CFBundleVersion
266 // that's safe for Chrome to load, along with the necessary product and
267 // contact information. Whitelisted bundles in this list may be removed if
268 // they are found to cause instability or otherwise behave badly. With
269 // proper contact information, Chrome developers may try to contact
270 // maintainers to resolve any problems.
271 const AllowedBundle kAllowedBundles[] = {
272 // Google Authenticator BT
273 // Dave MacLachlan <dmaclach@google.com>
274 { @"com.google.osax.Google_Authenticator_BT", nil },
275
276 // Default Folder X, http://www.stclairsoft.com/DefaultFolderX/
277 // Jon Gotow <gotow@stclairsoft.com>
278 { @"com.stclairsoft.DefaultFolderX", @"4.4.3" },
279
280 // MySpeed, http://www.enounce.com/myspeed
281 // Edward Bianchi <ejbianchi@enounce.com>
282 { @"com.enounce.MySpeed.osax", @"1201" },
283
284 // SIMBL (fork), https://github.com/albertz/simbl
285 // Albert Zeyer <albzey@googlemail.com>
286 { @"net.culater.SIMBL", nil },
287
288 // Smart Scroll, http://marcmoini.com/sx_en.html
289 // Marc Moini <marc@a9ff.com>
290 { @"com.marcmoini.SmartScroll", @"3.9" },
291 };
292
293 for (size_t index = 0; index < arraysize(kAllowedBundles); ++index) {
294 const AllowedBundle& allowed_bundle = kAllowedBundles[index];
295 NSString* allowed_bundle_id = allowed_bundle.bundle_id;
296 NSUInteger allowed_bundle_id_length = [allowed_bundle_id length];
297
298 // Permit bundle identifiers that are exactly equal to the allowed
299 // identifier, as well as "children" of the allowed identifier.
300 if ([bundle_id isEqualToString:allowed_bundle_id] ||
301 ([bundle_id length] > allowed_bundle_id_length &&
302 [bundle_id characterAtIndex:allowed_bundle_id_length] == '.' &&
303 [bundle_id hasPrefix:allowed_bundle_id])) {
304 NSString* minimum_version = allowed_bundle.minimum_version;
305 if (!minimum_version) {
306 // If the rule didn't declare any version requirement, the bundle is
307 // allowed to load.
308 return true;
309 }
310
311 if (!version) {
312 // If there wasn't any version but one was required, the bundle isn't
313 // allowed to load.
314 return false;
315 }
316
317 // A numeric search is appropriate for comparing version numbers.
318 NSComparisonResult result = [version compare:minimum_version
319 options:NSNumericSearch];
320 return result != NSOrderedAscending;
321 }
322 }
323
324 // Nothing matched.
325 return false;
326 }
327
236 } // namespace mac 328 } // namespace mac
237 } // namespace common 329 } // namespace common
238 } // namespace chrome 330 } // namespace chrome
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698