OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/installer/gcapi_mac/gcapi.h" | 5 #include "chrome/installer/gcapi_mac/gcapi.h" |
6 | 6 |
7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
| 8 #include <grp.h> |
| 9 #include <pwd.h> |
8 #include <sys/stat.h> | 10 #include <sys/stat.h> |
9 #include <sys/types.h> | 11 #include <sys/types.h> |
10 #include <sys/utsname.h> | 12 #include <sys/utsname.h> |
11 | 13 |
12 namespace { | 14 namespace { |
13 | 15 |
| 16 // The "~~" prefixes are replaced with the home directory of the |
| 17 // console owner (i.e. not the home directory of the euid). |
14 NSString* const kChromeInstallPath = @"/Applications/Google Chrome.app"; | 18 NSString* const kChromeInstallPath = @"/Applications/Google Chrome.app"; |
15 | 19 |
16 NSString* const kBrandKey = @"KSBrandID"; | 20 NSString* const kBrandKey = @"KSBrandID"; |
17 NSString* const kSystemBrandPath = @"/Library/Google/Google Chrome Brand.plist"; | 21 NSString* const kUserBrandPath = @"~~/Library/Google/Google Chrome Brand.plist"; |
18 NSString* const kUserBrandPath = @"~/Library/Google/Google Chrome Brand.plist"; | |
19 | 22 |
20 NSString* const kSystemKsadminPath = | 23 NSString* const kSystemKsadminPath = |
21 @"/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/" | 24 @"/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/" |
22 "Contents/MacOS/ksadmin"; | 25 "Contents/MacOS/ksadmin"; |
23 | 26 |
24 NSString* const kUserKsadminPath = | 27 NSString* const kUserKsadminPath = |
25 @"~/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/" | 28 @"~~/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/" |
26 "Contents/MacOS/ksadmin"; | 29 "Contents/MacOS/ksadmin"; |
27 | 30 |
28 NSString* const kSystemMasterPrefsPath = | 31 NSString* const kSystemMasterPrefsPath = |
29 @"/Library/Google/Google Chrome Master Preferences"; | 32 @"/Library/Google/Google Chrome Master Preferences"; |
30 NSString* const kUserMasterPrefsPath = | 33 NSString* const kUserMasterPrefsPath = |
31 @"~/Library/Application Support/Google/Google Chrome Master Preferences"; | 34 @"~~/Library/Application Support/Google/Google Chrome Master Preferences"; |
32 | 35 |
33 NSString* const kChannelKey = @"KSChannelID"; | 36 NSString* const kChannelKey = @"KSChannelID"; |
34 NSString* const kVersionKey = @"KSVersion"; | 37 NSString* const kVersionKey = @"KSVersion"; |
35 | 38 |
36 // Condensed from chromium's base/mac/mac_util.mm. | 39 // Condensed from chromium's base/mac/mac_util.mm. |
37 bool IsOSXVersionSupported() { | 40 bool IsOSXVersionSupported() { |
38 // On 10.6, Gestalt() was observed to be able to spawn threads (see | 41 // On 10.6, Gestalt() was observed to be able to spawn threads (see |
39 // http://crbug.com/53200). Don't call Gestalt(). | 42 // http://crbug.com/53200). Don't call Gestalt(). |
40 struct utsname uname_info; | 43 struct utsname uname_info; |
41 if (uname(&uname_info) != 0) | 44 if (uname(&uname_info) != 0) |
(...skipping 11 matching lines...) Expand all Loading... |
53 | 56 |
54 // The Darwin major version is always 4 greater than the Mac OS X minor | 57 // The Darwin major version is always 4 greater than the Mac OS X minor |
55 // version for Darwin versions beginning with 6, corresponding to Mac OS X | 58 // version for Darwin versions beginning with 6, corresponding to Mac OS X |
56 // 10.2. | 59 // 10.2. |
57 int mac_os_x_minor_version = darwin_major_version - 4; | 60 int mac_os_x_minor_version = darwin_major_version - 4; |
58 | 61 |
59 // Chrome is known to work on 10.6 - 10.8. | 62 // Chrome is known to work on 10.6 - 10.8. |
60 return mac_os_x_minor_version >= 6 && mac_os_x_minor_version <= 8; | 63 return mac_os_x_minor_version >= 6 && mac_os_x_minor_version <= 8; |
61 } | 64 } |
62 | 65 |
| 66 // Returns the pid/gid of the logged-in user, even if getuid() claims that the |
| 67 // current user is root. |
| 68 // Returns NULL on error. |
| 69 passwd* GetRealUserId() { |
| 70 CFDictionaryRef session_info_dict = CGSessionCopyCurrentDictionary(); |
| 71 [NSMakeCollectable(session_info_dict) autorelease]; |
| 72 if (!session_info_dict) |
| 73 return NULL; // Possibly no screen plugged in. |
| 74 |
| 75 CFNumberRef ns_uid = (CFNumberRef)CFDictionaryGetValue(session_info_dict, |
| 76 kCGSessionUserIDKey); |
| 77 if (CFGetTypeID(ns_uid) != CFNumberGetTypeID()) |
| 78 return NULL; |
| 79 |
| 80 uid_t uid; |
| 81 BOOL success = CFNumberGetValue(ns_uid, kCFNumberSInt32Type, &uid); |
| 82 if (!success) |
| 83 return NULL; |
| 84 |
| 85 return getpwuid(uid); |
| 86 } |
| 87 |
63 enum TicketKind { | 88 enum TicketKind { |
64 kSystemTicket, kUserTicket | 89 kSystemTicket, kUserTicket |
65 }; | 90 }; |
66 | 91 |
67 BOOL HasChromeTicket(TicketKind kind) { | 92 // Replaces "~~" with |home_dir|. |
| 93 NSString* AdjustHomedir(NSString* s, const char* home_dir) { |
| 94 if (![s hasPrefix:@"~~"]) |
| 95 return s; |
| 96 NSString* ns_home_dir = [NSString stringWithUTF8String:home_dir]; |
| 97 return [ns_home_dir stringByAppendingString:[s substringFromIndex:2]]; |
| 98 } |
| 99 |
| 100 BOOL HasChromeTicket(TicketKind kind, const passwd* user) { |
68 // Don't use Objective-C 2 loop syntax, in case an installer runs on 10.4. | 101 // Don't use Objective-C 2 loop syntax, in case an installer runs on 10.4. |
69 NSMutableArray* keystonePaths = | 102 NSMutableArray* keystone_paths = |
70 [NSMutableArray arrayWithObject:kSystemKsadminPath]; | 103 [NSMutableArray arrayWithObject:kSystemKsadminPath]; |
71 if (kind == kUserTicket && geteuid() != 0) | 104 if (kind == kUserTicket) { |
72 [keystonePaths insertObject:kUserKsadminPath atIndex:0]; | 105 [keystone_paths insertObject:AdjustHomedir(kUserKsadminPath, user->pw_dir) |
73 NSEnumerator* e = [keystonePaths objectEnumerator]; | 106 atIndex:0]; |
74 id ksPath; | 107 } |
75 while ((ksPath = [e nextObject])) { | 108 NSEnumerator* e = [keystone_paths objectEnumerator]; |
| 109 id ks_path; |
| 110 while ((ks_path = [e nextObject])) { |
76 NSTask* task = nil; | 111 NSTask* task = nil; |
77 NSString* string = nil; | 112 NSString* string = nil; |
78 bool ksadminRanSuccessfully = false; | 113 bool ksadmin_ran_successfully = false; |
79 | 114 |
80 @try { | 115 @try { |
81 task = [[NSTask alloc] init]; | 116 task = [[NSTask alloc] init]; |
82 [task setLaunchPath:ksPath]; | 117 [task setLaunchPath:ks_path]; |
83 | 118 |
84 NSArray* arguments = @[ | 119 NSArray* arguments = @[ |
85 kind == kUserTicket ? @"--user-store" : @"--system-store", | 120 kind == kUserTicket ? @"--user-store" : @"--system-store", |
86 @"--print-tickets", | 121 @"--print-tickets", |
87 @"--productid", | 122 @"--productid", |
88 @"com.google.Chrome", | 123 @"com.google.Chrome", |
89 ]; | 124 ]; |
| 125 if (geteuid() == 0 && kind == kUserTicket) { |
| 126 NSString* run_as = [NSString stringWithUTF8String:user->pw_name]; |
| 127 [task setLaunchPath:@"/usr/bin/sudo"]; |
| 128 arguments = [@[@"-u", run_as, ks_path] |
| 129 arrayByAddingObjectsFromArray:arguments]; |
| 130 } |
90 [task setArguments:arguments]; | 131 [task setArguments:arguments]; |
91 | 132 |
92 NSPipe* pipe = [NSPipe pipe]; | 133 NSPipe* pipe = [NSPipe pipe]; |
93 [task setStandardOutput:pipe]; | 134 [task setStandardOutput:pipe]; |
94 | 135 |
95 NSFileHandle* file = [pipe fileHandleForReading]; | 136 NSFileHandle* file = [pipe fileHandleForReading]; |
96 | 137 |
97 [task launch]; | 138 [task launch]; |
98 | 139 |
99 NSData* data = [file readDataToEndOfFile]; | 140 NSData* data = [file readDataToEndOfFile]; |
100 [task waitUntilExit]; | 141 [task waitUntilExit]; |
101 | 142 |
102 ksadminRanSuccessfully = [task terminationStatus] == 0; | 143 ksadmin_ran_successfully = [task terminationStatus] == 0; |
103 string = [[[NSString alloc] initWithData:data | 144 string = [[[NSString alloc] initWithData:data |
104 encoding:NSUTF8StringEncoding] autorelease]; | 145 encoding:NSUTF8StringEncoding] autorelease]; |
105 } | 146 } |
106 @catch (id exception) { | 147 @catch (id exception) { |
107 // Most likely, ksPath didn't exist. | 148 // Most likely, ks_path didn't exist. |
108 } | 149 } |
109 [task release]; | 150 [task release]; |
110 | 151 |
111 if (ksadminRanSuccessfully && [string length] > 0) | 152 if (ksadmin_ran_successfully && [string length] > 0) |
112 return YES; | 153 return YES; |
113 } | 154 } |
114 | 155 |
115 return NO; | 156 return NO; |
116 } | 157 } |
117 | 158 |
118 // Returns the file permission mask for files created by gcapi. | 159 // File permission mask for files created by gcapi. |
119 mode_t Permissions() { | 160 const mode_t kUserPermissions = 0755; |
120 return 0755; | 161 const mode_t kAdminPermissions = 0775; |
121 } | |
122 | 162 |
123 BOOL CreatePathToFile(NSString* path) { | 163 BOOL CreatePathToFile(NSString* path, const passwd* user) { |
124 path = [path stringByDeletingLastPathComponent]; | 164 path = [path stringByDeletingLastPathComponent]; |
125 | 165 |
126 // Default owner, group, permissions: | 166 // Default owner, group, permissions: |
127 // * Permissions are set according to the umask of the current process. For | 167 // * Permissions are set according to the umask of the current process. For |
128 // more information, see umask. | 168 // more information, see umask. |
129 // * The owner ID is set to the effective user ID of the process. | 169 // * The owner ID is set to the effective user ID of the process. |
130 // * The group ID is set to that of the parent directory. | 170 // * The group ID is set to that of the parent directory. |
131 // The default group ID is fine. Owner ID is fine too, since user directory | 171 // The default group ID is fine. Owner ID is fine if creating a system path, |
132 // paths won't be created if euid is 0. Do set permissions explicitly; for | 172 // but when creating a user path explicitly set the owner in case euid is 0. |
133 // admin paths all admins can write, for user paths just the owner may. | 173 // Do set permissions explicitly; for admin paths all admins can write, for |
134 NSMutableDictionary* attributes = [NSMutableDictionary | 174 // user paths just the owner may. |
135 dictionaryWithObject:[NSNumber numberWithShort:Permissions()] | 175 NSMutableDictionary* attributes = [NSMutableDictionary dictionary]; |
136 forKey:NSFilePosixPermissions]; | 176 if (user) { |
137 if (geteuid() == 0) | 177 [attributes setObject:[NSNumber numberWithShort:kUserPermissions] |
138 [attributes setObject:@"wheel" forKey:NSFileGroupOwnerAccountName]; | 178 forKey:NSFilePosixPermissions]; |
| 179 [attributes setObject:[NSNumber numberWithInt:user->pw_uid] |
| 180 forKey:NSFileOwnerAccountID]; |
| 181 } else { |
| 182 [attributes setObject:[NSNumber numberWithShort:kAdminPermissions] |
| 183 forKey:NSFilePosixPermissions]; |
| 184 [attributes setObject:@"admin" forKey:NSFileGroupOwnerAccountName]; |
| 185 } |
139 | 186 |
140 NSFileManager* manager = [NSFileManager defaultManager]; | 187 NSFileManager* manager = [NSFileManager defaultManager]; |
141 return [manager createDirectoryAtPath:path | 188 return [manager createDirectoryAtPath:path |
142 withIntermediateDirectories:YES | 189 withIntermediateDirectories:YES |
143 attributes:attributes | 190 attributes:attributes |
144 error:nil]; | 191 error:nil]; |
145 } | 192 } |
146 | 193 |
147 // Tries to write |data| at |system_path| or if that fails and geteuid() is not | 194 // Tries to write |data| at |user_path|. |
148 // 0 at |user_path|. Returns the path where it wrote, or nil on failure. | 195 // Returns the path where it wrote, or nil on failure. |
149 NSString* WriteData(NSData* data, NSString* system_path, NSString* user_path) { | 196 NSString* WriteUserData(NSData* data, |
| 197 NSString* user_path, |
| 198 const passwd* user) { |
| 199 user_path = AdjustHomedir(user_path, user->pw_dir); |
| 200 if (CreatePathToFile(user_path, user) && |
| 201 [data writeToFile:user_path atomically:YES]) { |
| 202 chmod([user_path fileSystemRepresentation], kUserPermissions & ~0111); |
| 203 chown([user_path fileSystemRepresentation], user->pw_uid, user->pw_gid); |
| 204 return user_path; |
| 205 } |
| 206 return nil; |
| 207 } |
| 208 |
| 209 // Tries to write |data| at |system_path| or if that fails at |user_path|. |
| 210 // Returns the path where it wrote, or nil on failure. |
| 211 NSString* WriteData(NSData* data, |
| 212 NSString* system_path, |
| 213 NSString* user_path, |
| 214 const passwd* user) { |
150 // Try system first. | 215 // Try system first. |
151 if (CreatePathToFile(system_path) && | 216 if (CreatePathToFile(system_path, NULL) && |
152 [data writeToFile:system_path atomically:YES]) { | 217 [data writeToFile:system_path atomically:YES]) { |
153 // files are created with group of parent dir (good), owner of euid (good). | 218 chmod([system_path fileSystemRepresentation], kAdminPermissions & ~0111); |
154 chmod([system_path fileSystemRepresentation], Permissions() & ~0111); | 219 // Make sure the file is owned by group admin. |
| 220 if (group* group = getgrnam("admin")) |
| 221 chown([system_path fileSystemRepresentation], 0, group->gr_gid); |
155 return system_path; | 222 return system_path; |
156 } | 223 } |
157 | 224 |
158 // Failed, try user. | 225 // Failed, try user. |
159 // -stringByExpandingTildeInPath returns root's home directory if this is run | 226 return WriteUserData(data, user_path, user); |
160 // setuid root, and in that case the kSystemBrandPath path above should have | |
161 // worked anyway. So only try user if geteuid() isn't root. | |
162 if (geteuid() != 0) { | |
163 user_path = [user_path stringByExpandingTildeInPath]; | |
164 if (CreatePathToFile(user_path) && | |
165 [data writeToFile:user_path atomically:YES]) { | |
166 chmod([user_path fileSystemRepresentation], Permissions() & ~0111); | |
167 return user_path; | |
168 } | |
169 } | |
170 return nil; | |
171 } | 227 } |
172 | 228 |
173 NSString* WriteBrandCode(const char* brand_code) { | 229 NSString* WriteBrandCode(const char* brand_code, const passwd* user) { |
174 NSDictionary* brand_dict = @{ | 230 NSDictionary* brand_dict = @{ |
175 kBrandKey: [NSString stringWithUTF8String:brand_code], | 231 kBrandKey: [NSString stringWithUTF8String:brand_code], |
176 }; | 232 }; |
177 NSData* contents = [NSPropertyListSerialization | 233 NSData* contents = [NSPropertyListSerialization |
178 dataFromPropertyList:brand_dict | 234 dataFromPropertyList:brand_dict |
179 format:NSPropertyListBinaryFormat_v1_0 | 235 format:NSPropertyListBinaryFormat_v1_0 |
180 errorDescription:nil]; | 236 errorDescription:nil]; |
181 | 237 |
182 return WriteData(contents, kSystemBrandPath, kUserBrandPath); | 238 return WriteUserData(contents, kUserBrandPath, user); |
183 } | 239 } |
184 | 240 |
185 BOOL WriteMasterPrefs(const char* master_prefs_contents, | 241 BOOL WriteMasterPrefs(const char* master_prefs_contents, |
186 size_t master_prefs_contents_size) { | 242 size_t master_prefs_contents_size, |
| 243 const passwd* user) { |
187 NSData* contents = [NSData dataWithBytes:master_prefs_contents | 244 NSData* contents = [NSData dataWithBytes:master_prefs_contents |
188 length:master_prefs_contents_size]; | 245 length:master_prefs_contents_size]; |
189 return | 246 return WriteData( |
190 WriteData(contents, kSystemMasterPrefsPath, kUserMasterPrefsPath) != nil; | 247 contents, kSystemMasterPrefsPath, kUserMasterPrefsPath, user) != nil; |
191 } | 248 } |
192 | 249 |
193 NSString* PathToFramework(NSString* app_path, NSDictionary* info_plist) { | 250 NSString* PathToFramework(NSString* app_path, NSDictionary* info_plist) { |
194 NSString* version = [info_plist objectForKey:@"CFBundleShortVersionString"]; | 251 NSString* version = [info_plist objectForKey:@"CFBundleShortVersionString"]; |
195 if (!version) | 252 if (!version) |
196 return nil; | 253 return nil; |
197 return [[[app_path | 254 return [[[app_path |
198 stringByAppendingPathComponent:@"Contents/Versions"] | 255 stringByAppendingPathComponent:@"Contents/Versions"] |
199 stringByAppendingPathComponent:version] | 256 stringByAppendingPathComponent:version] |
200 stringByAppendingPathComponent:@"Google Chrome Framework.framework"]; | 257 stringByAppendingPathComponent:@"Google Chrome Framework.framework"]; |
201 } | 258 } |
202 | 259 |
203 NSString* PathToInstallScript(NSString* app_path, NSDictionary* info_plist) { | 260 NSString* PathToInstallScript(NSString* app_path, NSDictionary* info_plist) { |
204 return [PathToFramework(app_path, info_plist) stringByAppendingPathComponent: | 261 return [PathToFramework(app_path, info_plist) stringByAppendingPathComponent: |
205 @"Resources/install.sh"]; | 262 @"Resources/install.sh"]; |
206 } | 263 } |
207 | 264 |
208 bool isbrandchar(int c) { | 265 bool isbrandchar(int c) { |
209 // Always four upper-case alpha chars. | 266 // Always four upper-case alpha chars. |
210 return c >= 'A' && c <= 'Z'; | 267 return c >= 'A' && c <= 'Z'; |
211 } | 268 } |
212 | 269 |
213 } // namespace | 270 } // namespace |
214 | 271 |
215 int GoogleChromeCompatibilityCheck(unsigned* reasons) { | 272 int GoogleChromeCompatibilityCheck(unsigned* reasons) { |
216 unsigned local_reasons = 0; | 273 unsigned local_reasons = 0; |
| 274 @autoreleasepool { |
| 275 passwd* user = GetRealUserId(); |
217 | 276 |
218 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; | 277 if (!IsOSXVersionSupported()) |
| 278 local_reasons |= GCCC_ERROR_OSNOTSUPPORTED; |
219 | 279 |
220 if (!IsOSXVersionSupported()) | 280 if (HasChromeTicket(kSystemTicket, NULL)) |
221 local_reasons |= GCCC_ERROR_OSNOTSUPPORTED; | 281 local_reasons |= GCCC_ERROR_ALREADYPRESENT; |
222 | 282 |
223 if (HasChromeTicket(kSystemTicket)) | 283 if (HasChromeTicket(kUserTicket, user)) |
224 local_reasons |= GCCC_ERROR_SYSTEMLEVELALREADYPRESENT; | 284 local_reasons |= GCCC_ERROR_ALREADYPRESENT; |
225 | 285 |
226 if (geteuid() != 0 && HasChromeTicket(kUserTicket)) | 286 if (![[NSFileManager defaultManager] isWritableFileAtPath:@"/Applications"]) |
227 local_reasons |= GCCC_ERROR_USERLEVELALREADYPRESENT; | 287 local_reasons |= GCCC_ERROR_ACCESSDENIED; |
228 | 288 |
229 if (![[NSFileManager defaultManager] isWritableFileAtPath:@"/Applications"]) | 289 if ([[NSFileManager defaultManager] fileExistsAtPath:kChromeInstallPath]) |
230 local_reasons |= GCCC_ERROR_ACCESSDENIED; | 290 local_reasons |= GCCC_ERROR_ALREADYPRESENT; |
231 | 291 } |
232 [pool drain]; | |
233 | |
234 // Done. Copy/return results. | |
235 if (reasons != NULL) | 292 if (reasons != NULL) |
236 *reasons = local_reasons; | 293 *reasons = local_reasons; |
237 | |
238 return local_reasons == 0; | 294 return local_reasons == 0; |
239 } | 295 } |
240 | 296 |
241 int InstallGoogleChrome(const char* source_path, | 297 int InstallGoogleChrome(const char* source_path, |
242 const char* brand_code, | 298 const char* brand_code, |
243 const char* master_prefs_contents, | 299 const char* master_prefs_contents, |
244 unsigned master_prefs_contents_size) { | 300 unsigned master_prefs_contents_size) { |
245 if (!GoogleChromeCompatibilityCheck(NULL)) | 301 if (!GoogleChromeCompatibilityCheck(NULL)) |
246 return 0; | 302 return 0; |
247 | 303 |
248 @autoreleasepool { | 304 @autoreleasepool { |
| 305 passwd* user = GetRealUserId(); |
| 306 |
249 NSString* app_path = [NSString stringWithUTF8String:source_path]; | 307 NSString* app_path = [NSString stringWithUTF8String:source_path]; |
250 NSString* info_plist_path = | 308 NSString* info_plist_path = |
251 [app_path stringByAppendingPathComponent:@"Contents/Info.plist"]; | 309 [app_path stringByAppendingPathComponent:@"Contents/Info.plist"]; |
252 NSDictionary* info_plist = | 310 NSDictionary* info_plist = |
253 [NSDictionary dictionaryWithContentsOfFile:info_plist_path]; | 311 [NSDictionary dictionaryWithContentsOfFile:info_plist_path]; |
254 | 312 |
255 // Use install.sh from the Chrome app bundle to copy Chrome to its | 313 // Use install.sh from the Chrome app bundle to copy Chrome to its |
256 // destination. | 314 // destination. |
257 NSString* install_script = PathToInstallScript(app_path, info_plist); | 315 NSString* install_script = PathToInstallScript(app_path, info_plist); |
258 if (!install_script) { | 316 if (!install_script) { |
259 return 0; | 317 return 0; |
260 } | 318 } |
261 | 319 |
262 @try { | 320 @try { |
| 321 // install.sh tries to make the installed app admin-writable, but |
| 322 // only when it's not run as root. |
| 323 NSString* run_as = [NSString stringWithUTF8String:user->pw_name]; |
263 NSTask* task = [[[NSTask alloc] init] autorelease]; | 324 NSTask* task = [[[NSTask alloc] init] autorelease]; |
264 [task setLaunchPath:install_script]; | 325 [task setLaunchPath:@"/usr/bin/sudo"]; |
265 [task setArguments:@[app_path, kChromeInstallPath]]; | 326 [task setArguments: |
| 327 @[@"-u", run_as, install_script, app_path, kChromeInstallPath]]; |
266 [task launch]; | 328 [task launch]; |
267 [task waitUntilExit]; | 329 [task waitUntilExit]; |
268 if ([task terminationStatus] != 0) { | 330 if ([task terminationStatus] != 0) { |
269 return 0; | 331 return 0; |
270 } | 332 } |
271 } | 333 } |
272 @catch (id exception) { | 334 @catch (id exception) { |
273 return 0; | 335 return 0; |
274 } | 336 } |
275 | 337 |
276 // Set brand code. If Chrome's Info.plist contains a brand code, use that. | 338 // Set brand code. If Chrome's Info.plist contains a brand code, use that. |
277 NSString* info_plist_brand = [info_plist objectForKey:kBrandKey]; | 339 NSString* info_plist_brand = [info_plist objectForKey:kBrandKey]; |
278 if (info_plist_brand && | 340 if (info_plist_brand && |
279 [info_plist_brand respondsToSelector:@selector(UTF8String)]) | 341 [info_plist_brand respondsToSelector:@selector(UTF8String)]) |
280 brand_code = [info_plist_brand UTF8String]; | 342 brand_code = [info_plist_brand UTF8String]; |
281 | 343 |
282 BOOL valid_brand_code = brand_code && strlen(brand_code) == 4 && | 344 BOOL valid_brand_code = brand_code && strlen(brand_code) == 4 && |
283 isbrandchar(brand_code[0]) && isbrandchar(brand_code[1]) && | 345 isbrandchar(brand_code[0]) && isbrandchar(brand_code[1]) && |
284 isbrandchar(brand_code[2]) && isbrandchar(brand_code[3]); | 346 isbrandchar(brand_code[2]) && isbrandchar(brand_code[3]); |
285 | 347 |
286 NSString* brand_path = nil; | 348 NSString* brand_path = nil; |
287 if (valid_brand_code) | 349 if (valid_brand_code) |
288 brand_path = WriteBrandCode(brand_code); | 350 brand_path = WriteBrandCode(brand_code, user); |
289 | 351 |
290 // Write master prefs. | 352 // Write master prefs. |
291 if (master_prefs_contents) | 353 if (master_prefs_contents) |
292 WriteMasterPrefs(master_prefs_contents, master_prefs_contents_size); | 354 WriteMasterPrefs(master_prefs_contents, master_prefs_contents_size, user); |
293 | 355 |
294 // TODO Set default browser if requested. Will be tricky when running as | 356 // TODO Set default browser if requested. |
295 // root. | |
296 } | 357 } |
297 return 1; | 358 return 1; |
298 } | 359 } |
299 | 360 |
300 int LaunchGoogleChrome() { | 361 int LaunchGoogleChrome() { |
301 @autoreleasepool { | 362 @autoreleasepool { |
| 363 // NSWorkspace launches processes as the current console owner, |
| 364 // even when running with euid of 0. |
302 return [[NSWorkspace sharedWorkspace] launchApplication:kChromeInstallPath]; | 365 return [[NSWorkspace sharedWorkspace] launchApplication:kChromeInstallPath]; |
303 } | 366 } |
304 } | 367 } |
OLD | NEW |