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