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

Side by Side Diff: chrome/common/sandbox_mac.mm

Issue 434077: Add regex escaping code to Mac sandbox implementation and re-enable the utility process on OS X. (Closed)
Patch Set: Sync to trunk Created 11 years 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
« no previous file with comments | « chrome/chrome_tests.gypi ('k') | chrome/common/sandbox_mac_unittest.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
OLDNEW
« no previous file with comments | « chrome/chrome_tests.gypi ('k') | chrome/common/sandbox_mac_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698