Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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/sandbox_mac.h" | 5 #include "chrome/common/sandbox_mac.h" |
| 6 | 6 |
| 7 #include "base/debug_util.h" | 7 #include "base/debug_util.h" |
| 8 | 8 |
| 9 #import <Cocoa/Cocoa.h> | 9 #import <Cocoa/Cocoa.h> |
| 10 extern "C" { | 10 extern "C" { |
| 11 #include <sandbox.h> | 11 #include <sandbox.h> |
| 12 } | 12 } |
| 13 | 13 |
| 14 #include "base/basictypes.h" | 14 #include "base/basictypes.h" |
| 15 #include "base/command_line.h" | 15 #include "base/command_line.h" |
| 16 #include "base/json/string_escape.h" | 16 #include "base/file_util.h" |
| 17 #include "base/mac_util.h" | 17 #include "base/mac_util.h" |
| 18 #include "base/scoped_cftyperef.h" | 18 #include "base/scoped_cftyperef.h" |
| 19 #include "base/scoped_nsautorelease_pool.h" | 19 #include "base/scoped_nsautorelease_pool.h" |
| 20 #include "base/string16.h" | 20 #include "base/string16.h" |
| 21 #include "base/sys_info.h" | 21 #include "base/sys_info.h" |
| 22 #include "base/sys_string_conversions.h" | 22 #include "base/sys_string_conversions.h" |
| 23 #include "base/utf_string_conversions.h" | |
| 23 #include "chrome/common/chrome_switches.h" | 24 #include "chrome/common/chrome_switches.h" |
| 25 #include "unicode/uchar.h" | |
| 26 | |
| 27 namespace { | |
| 28 | |
| 29 // Try to escape |c| as a "SingleEscapeCharacter" (\n, etc). If successful, | |
| 30 // returns true and appends the escape sequence to |dst|. | |
| 31 bool EscapeSingleChar(char c, std::string* dst) { | |
|
Mark Mentovai
2009/12/03 14:11:25
I think you should only have one dst->append in th
| |
| 32 switch (c) { | |
| 33 case '\b': | |
| 34 dst->append("\\b"); | |
| 35 break; | |
| 36 case '\f': | |
| 37 dst->append("\\f"); | |
| 38 break; | |
| 39 case '\n': | |
| 40 dst->append("\\n"); | |
| 41 break; | |
| 42 case '\r': | |
| 43 dst->append("\\r"); | |
| 44 break; | |
| 45 case '\t': | |
| 46 dst->append("\\t"); | |
| 47 break; | |
| 48 case '\\': | |
| 49 dst->append("\\\\"); | |
| 50 break; | |
| 51 case '"': | |
| 52 dst->append("\\\""); | |
| 53 break; | |
| 54 default: | |
| 55 return false; | |
| 56 } | |
| 57 return true; | |
| 58 } | |
| 59 | |
| 60 } // namespace | |
| 24 | 61 |
| 25 namespace sandbox { | 62 namespace sandbox { |
| 26 | 63 |
| 64 // Escape |str_utf8| for use in a plain string variable in a sandbox | |
| 65 // configuraton file. On return |dst| is set to the utf-8 encoded quoted | |
| 66 // output. | |
| 67 // Returns: true on success, false otherwise. | |
| 68 bool QuotePlainString(const std::string& str_utf8, std::string* dst) { | |
| 69 const char* src = str_utf8.c_str(); | |
| 70 int32_t length = str_utf8.length(); | |
| 71 int32_t position = 0; | |
| 72 while (position < length) { | |
|
Mark Mentovai
2009/12/03 14:11:25
Before entering the loop, clear dst.
| |
| 73 UChar32 c; | |
| 74 U8_NEXT(src, position, length, c); // Macro increments |position|. | |
| 75 DCHECK_GE(c, 0); | |
| 76 if (c < 0) | |
| 77 return false; | |
| 78 | |
| 79 if (c < 128) { // EscapeSingleChar only handles ASCII. | |
| 80 char as_char = static_cast<char>(c); | |
| 81 if (EscapeSingleChar(as_char, dst)) { | |
| 82 continue; | |
| 83 } | |
| 84 } | |
| 85 | |
| 86 if (c < 32 || c > 126) { | |
| 87 // Any characters that aren't printable ASCII get the \u treatment. | |
| 88 unsigned int as_uint = static_cast<unsigned int>(c); | |
| 89 StringAppendF(dst, "\\u%04X", as_uint); | |
| 90 continue; | |
| 91 } | |
| 92 | |
| 93 // If we got here we know that the character in question is strictly | |
| 94 // in the ASCII range so there's no need to do any kind of encoding | |
| 95 // conversion. | |
| 96 dst->push_back(static_cast<char>(c)); | |
| 97 } | |
| 98 return true; | |
| 99 } | |
| 100 | |
| 101 // Escape |str_utf8| for use in a regex literal in a sandbox | |
| 102 // configuraton file. On return |dst| is set to the utf-8 encoded quoted | |
| 103 // output. | |
| 104 // | |
| 105 // The implementation of this function is based on empirical testing of the | |
| 106 // OS X sandbox on 10.5.8 & 10.6.2 which is undocumented and subject to change. | |
| 107 // | |
| 108 // Note: If str_utf8 contains any characters < 32 || >125 then the function | |
| 109 // fails and false is returned. | |
| 110 // | |
| 111 // Returns: true on success, false otherwise. | |
| 112 bool QuoteStringForRegex(const std::string& str_utf8, std::string* dst) { | |
| 113 // List of chars with special meaning to regex. | |
| 114 // This list is derived from http://perldoc.perl.org/perlre.html . | |
| 115 const char regex_special_chars[] = { | |
| 116 '\\', | |
| 117 | |
| 118 // Metacharacters | |
| 119 '^', | |
| 120 '.', | |
| 121 '$', | |
| 122 '|', | |
| 123 '(', | |
| 124 ')', | |
| 125 '[', | |
| 126 ']', | |
| 127 | |
| 128 // Quantifiers | |
| 129 '*', | |
| 130 '+', | |
| 131 '?', | |
| 132 '{', | |
| 133 '}', | |
| 134 }; | |
| 135 | |
| 136 // Anchor regex at start of path. | |
| 137 dst->push_back('^'); | |
|
Mark Mentovai
2009/12/03 14:11:25
Don't push_back. Assign. You want to clear out d
| |
| 138 | |
| 139 const char* src = str_utf8.c_str(); | |
| 140 int32_t length = str_utf8.length(); | |
| 141 int32_t position = 0; | |
| 142 while (position < length) { | |
| 143 UChar32 c; | |
| 144 U8_NEXT(src, position, length, c); // Macro increments |position|. | |
| 145 DCHECK_GE(c, 0); | |
| 146 if (c < 0) | |
| 147 return false; | |
| 148 | |
| 149 // The Mac sandbox regex parser only handles printable ASCII characters. | |
| 150 // 33 >= c <= 126 | |
| 151 if (c < 32 || c > 125) { | |
| 152 return false; | |
| 153 } | |
| 154 | |
| 155 for (size_t i = 0; i < arraysize(regex_special_chars); ++i) { | |
| 156 if (c == regex_special_chars[i]) { | |
| 157 dst->push_back('\\'); | |
| 158 break; | |
| 159 } | |
| 160 } | |
| 161 | |
| 162 dst->push_back(static_cast<char>(c)); | |
| 163 } | |
| 164 | |
| 165 // Make sure last element of path is interpreted as a directory, leaving this | |
|
Mark Mentovai
2009/12/03 14:11:25
as a directory. Leaving this
(period and a new s
| |
| 166 // off would allow access to files if they start with the same name as the | |
| 167 // directory. | |
| 168 dst->append("(/|$)"); | |
| 169 | |
| 170 return true; | |
| 171 } | |
| 172 | |
| 27 // Warm up System APIs that empirically need to be accessed before the Sandbox | 173 // Warm up System APIs that empirically need to be accessed before the Sandbox |
| 28 // is turned on. | 174 // is turned on. |
| 29 // This method is layed out in blocks, each one containing a separate function | 175 // This method is layed out in blocks, each one containing a separate function |
| 30 // that needs to be warmed up. The OS version on which we found the need to | 176 // that needs to be warmed up. The OS version on which we found the need to |
| 31 // enable the function is also noted. | 177 // enable the function is also noted. |
| 32 // This function is tested on the following OS versions: | 178 // This function is tested on the following OS versions: |
| 33 // 10.5.6, 10.6.0 | 179 // 10.5.6, 10.6.0 |
| 34 void SandboxWarmup() { | 180 void SandboxWarmup() { |
| 35 base::ScopedNSAutoreleasePool scoped_pool; | 181 base::ScopedNSAutoreleasePool scoped_pool; |
| 36 | 182 |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 84 } | 230 } |
| 85 | 231 |
| 86 // Turns on the OS X sandbox for this process. | 232 // Turns on the OS X sandbox for this process. |
| 87 bool EnableSandbox(SandboxProcessType sandbox_type, | 233 bool EnableSandbox(SandboxProcessType sandbox_type, |
| 88 const FilePath& allowed_dir) { | 234 const FilePath& allowed_dir) { |
| 89 // Sanity - currently only SANDBOX_TYPE_UTILITY supports a directory being | 235 // Sanity - currently only SANDBOX_TYPE_UTILITY supports a directory being |
| 90 // passed in. | 236 // passed in. |
| 91 if (sandbox_type != SANDBOX_TYPE_UTILITY) { | 237 if (sandbox_type != SANDBOX_TYPE_UTILITY) { |
| 92 DCHECK(allowed_dir.empty()) | 238 DCHECK(allowed_dir.empty()) |
| 93 << "Only SANDBOX_TYPE_UTILITY allows a custom directory parameter."; | 239 << "Only SANDBOX_TYPE_UTILITY allows a custom directory parameter."; |
| 94 } else { | |
| 95 DCHECK(!allowed_dir.empty()) | |
| 96 << "SANDBOX_TYPE_UTILITY " | |
| 97 << "needs a custom directory parameter, but an empty one was provided."; | |
| 98 } | 240 } |
| 99 | |
| 100 // We use a custom sandbox definition file to lock things down as | 241 // We use a custom sandbox definition file to lock things down as |
| 101 // tightly as possible. | 242 // tightly as possible. |
| 102 // TODO(jeremy): Look at using include syntax to unify common parts of sandbox | 243 // TODO(jeremy): Look at using include syntax to unify common parts of sandbox |
| 103 // definition files. | 244 // definition files. |
| 104 NSString* sandbox_config_filename = nil; | 245 NSString* sandbox_config_filename = nil; |
| 105 switch (sandbox_type) { | 246 switch (sandbox_type) { |
| 106 case SANDBOX_TYPE_RENDERER: | 247 case SANDBOX_TYPE_RENDERER: |
| 107 sandbox_config_filename = @"renderer"; | 248 sandbox_config_filename = @"renderer"; |
| 108 break; | 249 break; |
| 109 case SANDBOX_TYPE_WORKER: | 250 case SANDBOX_TYPE_WORKER: |
| 110 sandbox_config_filename = @"worker"; | 251 sandbox_config_filename = @"worker"; |
| 111 break; | 252 break; |
| 112 case SANDBOX_TYPE_UTILITY: | 253 case SANDBOX_TYPE_UTILITY: |
| 113 sandbox_config_filename = @"utility"; | 254 sandbox_config_filename = @"utility"; |
| 114 break; | 255 break; |
| 115 default: | 256 default: |
| 116 NOTREACHED(); | 257 NOTREACHED(); |
| 117 return false; | 258 return false; |
| 118 } | 259 } |
| 119 | 260 |
| 120 NSString* sandbox_profile_path = | 261 NSString* sandbox_profile_path = |
| 121 [mac_util::MainAppBundle() pathForResource:sandbox_config_filename | 262 [mac_util::MainAppBundle() pathForResource:sandbox_config_filename |
| 122 ofType:@"sb"]; | 263 ofType:@"sb"]; |
| 123 NSString* sandbox_data = [NSString | 264 NSString* sandbox_data = [NSString |
| 124 stringWithContentsOfFile:sandbox_profile_path | 265 stringWithContentsOfFile:sandbox_profile_path |
| 125 encoding:NSUTF8StringEncoding | 266 encoding:NSUTF8StringEncoding |
| 126 error:nil]; | 267 error:nil]; |
| 127 | 268 |
| 128 if (!sandbox_data) { | 269 if (!sandbox_data) { |
| 129 LOG(ERROR) << "Failed to find the sandbox profile on disk"; | 270 PLOG(ERROR) << "Failed to find the sandbox profile on disk " |
| 271 << base::SysNSStringToUTF8(sandbox_profile_path); | |
|
Mark Mentovai
2009/12/03 14:11:25
Align <<s.
| |
| 130 return false; | 272 return false; |
| 131 } | 273 } |
| 132 | 274 |
| 133 // Enable verbose logging if enabled on the command line. | 275 // Enable verbose logging if enabled on the command line. |
| 134 // (see renderer.sb for details). | 276 // (see renderer.sb for details). |
| 135 const CommandLine *command_line = CommandLine::ForCurrentProcess(); | 277 const CommandLine *command_line = CommandLine::ForCurrentProcess(); |
| 136 if (command_line->HasSwitch(switches::kEnableSandboxLogging)) { | 278 if (command_line->HasSwitch(switches::kEnableSandboxLogging)) { |
| 137 sandbox_data = [sandbox_data | 279 sandbox_data = [sandbox_data |
| 138 stringByReplacingOccurrencesOfString:@";ENABLE_LOGGING" | 280 stringByReplacingOccurrencesOfString:@";ENABLE_LOGGING" |
| 139 withString:@""]; | 281 withString:@""]; |
| 140 } | 282 } |
| 141 | 283 |
| 142 if (!allowed_dir.empty()) { | 284 if (!allowed_dir.empty()) { |
| 143 NSString* allowed_dir_ns = base::SysUTF8ToNSString(allowed_dir.value()); | 285 // The sandbox only understands "real" paths. This resolving step is |
| 286 // needed so the caller doesn't need to worry about things like /var | |
| 287 // being a link to /private/var (like in the paths CreateNewTempDirectory() | |
| 288 // returns). | |
| 289 FilePath allowed_dir_absolute(allowed_dir); | |
| 290 if (!file_util::AbsolutePath(&allowed_dir_absolute)) { | |
| 291 PLOG(ERROR) << "Failed to resolve absolute path error: "; | |
|
Mark Mentovai
2009/12/03 14:11:25
PLOG will take care of appending the error and any
| |
| 292 return false; | |
| 293 } | |
| 294 | |
| 295 std::string allowed_dir_escaped; | |
| 296 if (!QuoteStringForRegex(allowed_dir_absolute.value(), | |
| 297 &allowed_dir_escaped)) { | |
|
Mark Mentovai
2009/12/03 14:11:25
&allowed_dir_excaped should line up with allowed_d
| |
| 298 LOG(ERROR) << "Regex string quoting failed " << allowed_dir.value(); | |
| 299 return false; | |
| 300 } | |
| 301 NSString* allowed_dir_escaped_ns = base::SysUTF8ToNSString( | |
| 302 allowed_dir_escaped.c_str()); | |
| 303 sandbox_data = [sandbox_data | |
| 304 stringByReplacingOccurrencesOfString:@";ENABLE_DIRECTORY_ACCESS" | |
| 305 withString:@""]; | |
| 144 sandbox_data = [sandbox_data | 306 sandbox_data = [sandbox_data |
| 145 stringByReplacingOccurrencesOfString:@"DIR_TO_ALLOW_ACCESS" | 307 stringByReplacingOccurrencesOfString:@"DIR_TO_ALLOW_ACCESS" |
| 146 withString:allowed_dir_ns]; | 308 withString:allowed_dir_escaped_ns]; |
| 309 | |
| 147 } | 310 } |
| 148 | 311 |
| 149 int32 major_version, minor_version, bugfix_version; | 312 int32 major_version, minor_version, bugfix_version; |
| 150 base::SysInfo::OperatingSystemVersionNumbers(&major_version, | 313 base::SysInfo::OperatingSystemVersionNumbers(&major_version, |
| 151 &minor_version, &bugfix_version); | 314 &minor_version, &bugfix_version); |
| 152 | 315 |
| 153 if (major_version > 10 || (major_version == 10 && minor_version >= 6)) { | 316 if (major_version > 10 || (major_version == 10 && minor_version >= 6)) { |
| 154 // 10.6-only Sandbox rules. | 317 // 10.6-only Sandbox rules. |
| 155 sandbox_data = [sandbox_data | 318 sandbox_data = [sandbox_data |
| 156 stringByReplacingOccurrencesOfString:@";10.6_ONLY" | 319 stringByReplacingOccurrencesOfString:@";10.6_ONLY" |
| 157 withString:@""]; | 320 withString:@""]; |
| 158 // Splice the path of the user's home directory into the sandbox profile | 321 // Splice the path of the user's home directory into the sandbox profile |
| 159 // (see renderer.sb for details). | 322 // (see renderer.sb for details). |
| 160 // This code is in the 10.6-only block because the sandbox syntax we use | 323 // This code is in the 10.6-only block because the sandbox syntax we use |
| 161 // for this "subdir" is only supported on 10.6. | 324 // for this "subdir" is only supported on 10.6. |
| 162 // If we ever need this on pre-10.6 OSs then we'll have to rethink the | 325 // If we ever need this on pre-10.6 OSs then we'll have to rethink the |
| 163 // surrounding sandbox syntax. | 326 // surrounding sandbox syntax. |
| 164 string16 home_dir = base::SysNSStringToUTF16(NSHomeDirectory()); | 327 std::string home_dir = base::SysNSStringToUTF8(NSHomeDirectory()); |
| 165 std::string home_dir_escaped; | 328 std::string home_dir_escaped; |
| 166 base::JsonDoubleQuote(home_dir, false, &home_dir_escaped); | 329 if (!QuotePlainString(home_dir, &home_dir_escaped)) { |
| 330 LOG(ERROR) << "Sandbox string quoting failed"; | |
| 331 return false; | |
| 332 } | |
| 167 NSString* home_dir_escaped_ns = base::SysUTF8ToNSString(home_dir_escaped); | 333 NSString* home_dir_escaped_ns = base::SysUTF8ToNSString(home_dir_escaped); |
| 168 sandbox_data = [sandbox_data | 334 sandbox_data = [sandbox_data |
| 169 stringByReplacingOccurrencesOfString:@"USER_HOMEDIR" | 335 stringByReplacingOccurrencesOfString:@"USER_HOMEDIR" |
| 170 withString:home_dir_escaped_ns]; | 336 withString:home_dir_escaped_ns]; |
| 171 } | 337 } |
| 172 | 338 |
| 173 char* error_buff = NULL; | 339 char* error_buff = NULL; |
| 174 int error = sandbox_init([sandbox_data UTF8String], 0, &error_buff); | 340 int error = sandbox_init([sandbox_data UTF8String], 0, &error_buff); |
| 175 bool success = (error == 0 && error_buff == NULL); | 341 bool success = (error == 0 && error_buff == NULL); |
| 176 if (error == -1) { | 342 LOG_IF(ERROR, !success) << "Failed to Initialize Sandbox: " |
|
Mark Mentovai
2009/12/03 14:11:25
initialize and sandbox should be lowercase.
| |
| 177 LOG(ERROR) << "Failed to Initialize Sandbox: " << error_buff; | 343 << error |
| 178 } | 344 << " " |
| 345 << error_buff; | |
| 179 sandbox_free_error(error_buff); | 346 sandbox_free_error(error_buff); |
| 180 return success; | 347 return success; |
| 181 } | 348 } |
| 182 | 349 |
| 183 } // namespace sandbox | 350 } // namespace sandbox |
| OLD | NEW |