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 sessionInfoDict = CGSessionCopyCurrentDictionary(); | |
70 if (!sessionInfoDict) | |
71 return NULL; // Possibly no screen plugged in. | |
72 | |
73 CFNumberRef ns_uid = (CFNumberRef)CFDictionaryGetValue(sessionInfoDict, | |
74 kCGSessionUserIDKey); | |
75 if (CFGetTypeID(ns_uid) != CFNumberGetTypeID()) | |
76 return NULL; | |
Mark Mentovai
2012/08/13 15:59:25
But you haven’t released sessionInfoDict (which sh
Nico
2012/08/13 18:03:58
Done.
| |
77 | |
78 uid_t uid; | |
79 BOOL success = CFNumberGetValue(ns_uid, kCFNumberSInt32Type, &uid); | |
80 CFRelease(sessionInfoDict); | |
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]; |
106 } | |
107 NSEnumerator* e = [keystone_paths objectEnumerator]; | |
74 id ksPath; | 108 id ksPath; |
Mark Mentovai
2012/08/13 15:59:25
Naming again.
Nico
2012/08/13 18:03:58
Done.
| |
75 while ((ksPath = [e nextObject])) { | 109 while ((ksPath = [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 { |
115 // XXX does this need to run as other user? or can admin read the user sto re? | |
Mark Mentovai
2012/08/13 15:59:25
This XXX is now obsolete?
Nico
2012/08/13 18:03:58
Done.
| |
116 // it should be able to, so I think this is fine as is? | |
81 task = [[NSTask alloc] init]; | 117 task = [[NSTask alloc] init]; |
82 [task setLaunchPath:ksPath]; | 118 [task setLaunchPath:ksPath]; |
83 | 119 |
84 NSArray* arguments = @[ | 120 NSArray* arguments = @[ |
85 kind == kUserTicket ? @"--user-store" : @"--system-store", | 121 kind == kUserTicket ? @"--user-store" : @"--system-store", |
86 @"--print-tickets", | 122 @"--print-tickets", |
87 @"--productid", | 123 @"--productid", |
88 @"com.google.Chrome", | 124 @"com.google.Chrome", |
89 ]; | 125 ]; |
126 if (geteuid() == 0 && kind == kUserTicket) { | |
127 NSString* run_as = [NSString stringWithUTF8String:user->pw_name]; | |
128 [task setLaunchPath:@"/usr/bin/sudo"]; | |
129 arguments = [ | |
Mark Mentovai
2012/08/13 15:59:25
Weird indentation.
Nico
2012/08/13 18:03:58
Made it weird in a different way.
| |
130 @[@"-u", run_as, ksPath] arrayByAddingObjectsFromArray:arguments]; | |
131 } | |
90 [task setArguments:arguments]; | 132 [task setArguments:arguments]; |
91 | 133 |
92 NSPipe* pipe = [NSPipe pipe]; | 134 NSPipe* pipe = [NSPipe pipe]; |
93 [task setStandardOutput:pipe]; | 135 [task setStandardOutput:pipe]; |
94 | 136 |
95 NSFileHandle* file = [pipe fileHandleForReading]; | 137 NSFileHandle* file = [pipe fileHandleForReading]; |
96 | 138 |
97 [task launch]; | 139 [task launch]; |
98 | 140 |
99 NSData* data = [file readDataToEndOfFile]; | 141 NSData* data = [file readDataToEndOfFile]; |
100 [task waitUntilExit]; | 142 [task waitUntilExit]; |
101 | 143 |
102 ksadminRanSuccessfully = [task terminationStatus] == 0; | 144 ksadmin_ran_successfully = [task terminationStatus] == 0; |
103 string = [[[NSString alloc] initWithData:data | 145 string = [[[NSString alloc] initWithData:data |
104 encoding:NSUTF8StringEncoding] autorelease]; | 146 encoding:NSUTF8StringEncoding] autorelease]; |
105 } | 147 } |
106 @catch (id exception) { | 148 @catch (id exception) { |
107 // Most likely, ksPath didn't exist. | 149 // Most likely, ksPath didn't exist. |
108 } | 150 } |
109 [task release]; | 151 [task release]; |
110 | 152 |
111 if (ksadminRanSuccessfully && [string length] > 0) | 153 if (ksadmin_ran_successfully && [string length] > 0) |
112 return YES; | 154 return YES; |
113 } | 155 } |
114 | 156 |
115 return NO; | 157 return NO; |
116 } | 158 } |
117 | 159 |
118 // Returns the file permission mask for files created by gcapi. | 160 // File permission mask for files created by gcapi. |
119 mode_t Permissions() { | 161 const mode_t kUserPermissions = 0755; |
120 return 0755; | 162 const mode_t kAdminPermissions = 0775; |
121 } | |
122 | 163 |
123 BOOL CreatePathToFile(NSString* path) { | 164 BOOL CreatePathToFile(NSString* path, const passwd* user) { |
124 path = [path stringByDeletingLastPathComponent]; | 165 path = [path stringByDeletingLastPathComponent]; |
125 | 166 |
126 // Default owner, group, permissions: | 167 // Default owner, group, permissions: |
127 // * Permissions are set according to the umask of the current process. For | 168 // * Permissions are set according to the umask of the current process. For |
128 // more information, see umask. | 169 // more information, see umask. |
129 // * The owner ID is set to the effective user ID of the process. | 170 // * 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. | 171 // * 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 | 172 // 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 | 173 // 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. | 174 // Do set permissions explicitly; for admin paths all admins can write, for |
134 NSMutableDictionary* attributes = [NSMutableDictionary | 175 // user paths just the owner may. |
135 dictionaryWithObject:[NSNumber numberWithShort:Permissions()] | 176 NSMutableDictionary* attributes = [NSMutableDictionary dictionary]; |
136 forKey:NSFilePosixPermissions]; | 177 if (user) { |
137 if (geteuid() == 0) | 178 [attributes setObject:[NSNumber numberWithShort:kUserPermissions] |
138 [attributes setObject:@"wheel" forKey:NSFileGroupOwnerAccountName]; | 179 forKey:NSFilePosixPermissions]; |
180 [attributes setObject:[NSNumber numberWithInt:user->pw_uid] | |
181 forKey:NSFileOwnerAccountID]; | |
182 // XXX do this? not needed i think? | |
Mark Mentovai
2012/08/13 15:59:25
Right, shouldn’t be needed.
| |
183 //[attributes setObject:[NSNumber numberWithInt:user->pw_gid] | |
184 //forKey:NSFileGroupOwnerAccountID]; | |
185 } else { | |
186 [attributes setObject:[NSNumber numberWithShort:kAdminPermissions] | |
187 forKey:NSFilePosixPermissions]; | |
188 [attributes setObject:@"admin" forKey:NSFileGroupOwnerAccountName]; | |
189 } | |
139 | 190 |
140 NSFileManager* manager = [NSFileManager defaultManager]; | 191 NSFileManager* manager = [NSFileManager defaultManager]; |
141 return [manager createDirectoryAtPath:path | 192 return [manager createDirectoryAtPath:path |
142 withIntermediateDirectories:YES | 193 withIntermediateDirectories:YES |
143 attributes:attributes | 194 attributes:attributes |
144 error:nil]; | 195 error:nil]; |
145 } | 196 } |
146 | 197 |
147 // Tries to write |data| at |system_path| or if that fails and geteuid() is not | 198 // Tries to write |data| at |user_path|. |
148 // 0 at |user_path|. Returns the path where it wrote, or nil on failure. | 199 // Returns the path where it wrote, or nil on failure. |
149 NSString* WriteData(NSData* data, NSString* system_path, NSString* user_path) { | 200 NSString* WriteUserData(NSData* data, |
201 NSString* user_path, | |
202 const passwd* user) { | |
203 user_path = AdjustHomedir(user_path, user->pw_dir); | |
204 if (CreatePathToFile(user_path, user) && | |
205 [data writeToFile:user_path atomically:YES]) { | |
206 chmod([user_path fileSystemRepresentation], kUserPermissions & ~0111); | |
207 chown([user_path fileSystemRepresentation], user->pw_uid, user->pw_gid); | |
208 return user_path; | |
209 } | |
210 return nil; | |
211 } | |
212 | |
213 // Tries to write |data| at |system_path| or if that fails at |user_path|. | |
214 // Returns the path where it wrote, or nil on failure. | |
215 NSString* WriteData(NSData* data, | |
216 NSString* system_path, | |
217 NSString* user_path, | |
218 const passwd* user) { | |
150 // Try system first. | 219 // Try system first. |
151 if (CreatePathToFile(system_path) && | 220 if (CreatePathToFile(system_path, NULL) && |
152 [data writeToFile:system_path atomically:YES]) { | 221 [data writeToFile:system_path atomically:YES]) { |
153 // files are created with group of parent dir (good), owner of euid (good). | 222 // files are created with group of parent dir (good), owner of euid (good). |
Mark Mentovai
2012/08/13 15:59:25
You have no assurance what the group of the parent
Nico
2012/08/13 18:03:58
The directory is /Library/Google -- if that exist
Mark Mentovai
2012/08/13 18:43:24
Nico wrote:
| |
154 chmod([system_path fileSystemRepresentation], Permissions() & ~0111); | 223 chmod([system_path fileSystemRepresentation], kAdminPermissions & ~0111); |
155 return system_path; | 224 return system_path; |
156 } | 225 } |
157 | 226 |
158 // Failed, try user. | 227 // Failed, try user. |
159 // -stringByExpandingTildeInPath returns root's home directory if this is run | 228 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 } | 229 } |
172 | 230 |
173 NSString* WriteBrandCode(const char* brand_code) { | 231 NSString* WriteBrandCode(const char* brand_code, const passwd* user) { |
174 NSDictionary* brand_dict = @{ | 232 NSDictionary* brand_dict = @{ |
175 kBrandKey: [NSString stringWithUTF8String:brand_code], | 233 kBrandKey: [NSString stringWithUTF8String:brand_code], |
176 }; | 234 }; |
177 NSData* contents = [NSPropertyListSerialization | 235 NSData* contents = [NSPropertyListSerialization |
178 dataFromPropertyList:brand_dict | 236 dataFromPropertyList:brand_dict |
179 format:NSPropertyListBinaryFormat_v1_0 | 237 format:NSPropertyListBinaryFormat_v1_0 |
180 errorDescription:nil]; | 238 errorDescription:nil]; |
181 | 239 |
182 return WriteData(contents, kSystemBrandPath, kUserBrandPath); | 240 return WriteUserData(contents, kUserBrandPath, user); |
183 } | 241 } |
184 | 242 |
185 BOOL WriteMasterPrefs(const char* master_prefs_contents, | 243 BOOL WriteMasterPrefs(const char* master_prefs_contents, |
186 size_t master_prefs_contents_size) { | 244 size_t master_prefs_contents_size, |
245 const passwd* user) { | |
187 NSData* contents = [NSData dataWithBytes:master_prefs_contents | 246 NSData* contents = [NSData dataWithBytes:master_prefs_contents |
188 length:master_prefs_contents_size]; | 247 length:master_prefs_contents_size]; |
189 return | 248 return WriteData( |
190 WriteData(contents, kSystemMasterPrefsPath, kUserMasterPrefsPath) != nil; | 249 contents, kSystemMasterPrefsPath, kUserMasterPrefsPath, user) != nil; |
191 } | 250 } |
192 | 251 |
193 NSString* PathToFramework(NSString* app_path, NSDictionary* info_plist) { | 252 NSString* PathToFramework(NSString* app_path, NSDictionary* info_plist) { |
194 NSString* version = [info_plist objectForKey:@"CFBundleShortVersionString"]; | 253 NSString* version = [info_plist objectForKey:@"CFBundleShortVersionString"]; |
195 if (!version) | 254 if (!version) |
196 return nil; | 255 return nil; |
197 return [[[app_path | 256 return [[[app_path |
198 stringByAppendingPathComponent:@"Contents/Versions"] | 257 stringByAppendingPathComponent:@"Contents/Versions"] |
199 stringByAppendingPathComponent:version] | 258 stringByAppendingPathComponent:version] |
200 stringByAppendingPathComponent:@"Google Chrome Framework.framework"]; | 259 stringByAppendingPathComponent:@"Google Chrome Framework.framework"]; |
201 } | 260 } |
202 | 261 |
203 NSString* PathToInstallScript(NSString* app_path, NSDictionary* info_plist) { | 262 NSString* PathToInstallScript(NSString* app_path, NSDictionary* info_plist) { |
204 return [PathToFramework(app_path, info_plist) stringByAppendingPathComponent: | 263 return [PathToFramework(app_path, info_plist) stringByAppendingPathComponent: |
205 @"Resources/install.sh"]; | 264 @"Resources/install.sh"]; |
206 } | 265 } |
207 | 266 |
208 bool isbrandchar(int c) { | 267 bool isbrandchar(int c) { |
209 // Always four upper-case alpha chars. | 268 // Always four upper-case alpha chars. |
210 return c >= 'A' && c <= 'Z'; | 269 return c >= 'A' && c <= 'Z'; |
211 } | 270 } |
212 | 271 |
213 } // namespace | 272 } // namespace |
214 | 273 |
215 int GoogleChromeCompatibilityCheck(unsigned* reasons) { | 274 int GoogleChromeCompatibilityCheck(unsigned* reasons) { |
275 passwd* user = GetRealUserId(); | |
276 | |
216 unsigned local_reasons = 0; | 277 unsigned local_reasons = 0; |
217 | 278 |
218 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; | 279 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; |
219 | 280 |
220 if (!IsOSXVersionSupported()) | 281 if (!IsOSXVersionSupported()) |
221 local_reasons |= GCCC_ERROR_OSNOTSUPPORTED; | 282 local_reasons |= GCCC_ERROR_OSNOTSUPPORTED; |
222 | 283 |
223 if (HasChromeTicket(kSystemTicket)) | 284 if (HasChromeTicket(kSystemTicket, NULL)) |
224 local_reasons |= GCCC_ERROR_SYSTEMLEVELALREADYPRESENT; | 285 local_reasons |= GCCC_ERROR_ALREADYPRESENT; |
225 | 286 |
226 if (geteuid() != 0 && HasChromeTicket(kUserTicket)) | 287 if (HasChromeTicket(kUserTicket, user)) |
227 local_reasons |= GCCC_ERROR_USERLEVELALREADYPRESENT; | 288 local_reasons |= GCCC_ERROR_ALREADYPRESENT; |
228 | 289 |
229 if (![[NSFileManager defaultManager] isWritableFileAtPath:@"/Applications"]) | 290 if (![[NSFileManager defaultManager] isWritableFileAtPath:@"/Applications"]) |
230 local_reasons |= GCCC_ERROR_ACCESSDENIED; | 291 local_reasons |= GCCC_ERROR_ACCESSDENIED; |
231 | 292 |
293 if (!local_reasons && [[NSFileManager defaultManager] | |
Mark Mentovai
2012/08/13 15:59:25
Why are you checking !local_reasons here, when in
Nico
2012/08/13 18:03:58
Done. I thought that ACCESSDENIED shouldn't be set
| |
294 fileExistsAtPath:@"/Applications/Google Chrome.app"]) { | |
295 local_reasons |= GCCC_ERROR_ALREADYPRESENT; | |
296 } | |
297 | |
232 [pool drain]; | 298 [pool drain]; |
233 | 299 |
234 // Done. Copy/return results. | 300 // Done. Copy/return results. |
235 if (reasons != NULL) | 301 if (reasons != NULL) |
236 *reasons = local_reasons; | 302 *reasons = local_reasons; |
237 | 303 |
238 return local_reasons == 0; | 304 return local_reasons == 0; |
239 } | 305 } |
240 | 306 |
241 int InstallGoogleChrome(const char* source_path, | 307 int InstallGoogleChrome(const char* source_path, |
242 const char* brand_code, | 308 const char* brand_code, |
243 const char* master_prefs_contents, | 309 const char* master_prefs_contents, |
244 unsigned master_prefs_contents_size) { | 310 unsigned master_prefs_contents_size) { |
245 if (!GoogleChromeCompatibilityCheck(NULL)) | 311 if (!GoogleChromeCompatibilityCheck(NULL)) |
246 return 0; | 312 return 0; |
247 | 313 |
314 passwd* user = GetRealUserId(); | |
315 | |
248 @autoreleasepool { | 316 @autoreleasepool { |
249 NSString* app_path = [NSString stringWithUTF8String:source_path]; | 317 NSString* app_path = [NSString stringWithUTF8String:source_path]; |
250 NSString* info_plist_path = | 318 NSString* info_plist_path = |
251 [app_path stringByAppendingPathComponent:@"Contents/Info.plist"]; | 319 [app_path stringByAppendingPathComponent:@"Contents/Info.plist"]; |
252 NSDictionary* info_plist = | 320 NSDictionary* info_plist = |
253 [NSDictionary dictionaryWithContentsOfFile:info_plist_path]; | 321 [NSDictionary dictionaryWithContentsOfFile:info_plist_path]; |
254 | 322 |
255 // Use install.sh from the Chrome app bundle to copy Chrome to its | 323 // Use install.sh from the Chrome app bundle to copy Chrome to its |
256 // destination. | 324 // destination. |
257 NSString* install_script = PathToInstallScript(app_path, info_plist); | 325 NSString* install_script = PathToInstallScript(app_path, info_plist); |
258 if (!install_script) { | 326 if (!install_script) { |
259 return 0; | 327 return 0; |
260 } | 328 } |
261 | 329 |
262 @try { | 330 @try { |
331 // install.sh tries to make the installed app admin-writable, but | |
332 // only when it's not run as root. | |
333 NSString* run_as = [NSString stringWithUTF8String:user->pw_name]; | |
263 NSTask* task = [[[NSTask alloc] init] autorelease]; | 334 NSTask* task = [[[NSTask alloc] init] autorelease]; |
264 [task setLaunchPath:install_script]; | 335 [task setLaunchPath:@"/usr/bin/sudo"]; |
265 [task setArguments:@[app_path, kChromeInstallPath]]; | 336 [task setArguments: |
337 @[@"-u", run_as, install_script, app_path, kChromeInstallPath]]; | |
Mark Mentovai
2012/08/13 15:59:25
But if run_as is not an admin user, it won’t be ab
Nico
2012/08/13 18:03:58
Right, the new approach won't be able to install c
| |
266 [task launch]; | 338 [task launch]; |
267 [task waitUntilExit]; | 339 [task waitUntilExit]; |
268 if ([task terminationStatus] != 0) { | 340 if ([task terminationStatus] != 0) { |
269 return 0; | 341 return 0; |
270 } | 342 } |
271 } | 343 } |
272 @catch (id exception) { | 344 @catch (id exception) { |
273 return 0; | 345 return 0; |
274 } | 346 } |
275 | 347 |
276 // Set brand code. If Chrome's Info.plist contains a brand code, use that. | 348 // Set brand code. If Chrome's Info.plist contains a brand code, use that. |
277 NSString* info_plist_brand = [info_plist objectForKey:kBrandKey]; | 349 NSString* info_plist_brand = [info_plist objectForKey:kBrandKey]; |
278 if (info_plist_brand && | 350 if (info_plist_brand && |
279 [info_plist_brand respondsToSelector:@selector(UTF8String)]) | 351 [info_plist_brand respondsToSelector:@selector(UTF8String)]) |
280 brand_code = [info_plist_brand UTF8String]; | 352 brand_code = [info_plist_brand UTF8String]; |
281 | 353 |
282 BOOL valid_brand_code = brand_code && strlen(brand_code) == 4 && | 354 BOOL valid_brand_code = brand_code && strlen(brand_code) == 4 && |
283 isbrandchar(brand_code[0]) && isbrandchar(brand_code[1]) && | 355 isbrandchar(brand_code[0]) && isbrandchar(brand_code[1]) && |
284 isbrandchar(brand_code[2]) && isbrandchar(brand_code[3]); | 356 isbrandchar(brand_code[2]) && isbrandchar(brand_code[3]); |
285 | 357 |
286 NSString* brand_path = nil; | 358 NSString* brand_path = nil; |
287 if (valid_brand_code) | 359 if (valid_brand_code) |
288 brand_path = WriteBrandCode(brand_code); | 360 brand_path = WriteBrandCode(brand_code, user); |
289 | 361 |
290 // Write master prefs. | 362 // Write master prefs. |
291 if (master_prefs_contents) | 363 if (master_prefs_contents) |
292 WriteMasterPrefs(master_prefs_contents, master_prefs_contents_size); | 364 WriteMasterPrefs(master_prefs_contents, master_prefs_contents_size, user); |
293 | 365 |
294 // TODO Set default browser if requested. Will be tricky when running as | 366 // TODO Set default browser if requested. |
295 // root. | |
296 } | 367 } |
297 return 1; | 368 return 1; |
298 } | 369 } |
299 | 370 |
300 int LaunchGoogleChrome() { | 371 int LaunchGoogleChrome() { |
372 //passwd* user = GetRealUserId(); | |
Mark Mentovai
2012/08/13 15:59:25
Not needed?
Nico
2012/08/13 18:03:58
Done.
| |
373 | |
301 @autoreleasepool { | 374 @autoreleasepool { |
375 // NSWorkspace launches processes as the current console owner, | |
376 // even when running with euid of 0. | |
302 return [[NSWorkspace sharedWorkspace] launchApplication:kChromeInstallPath]; | 377 return [[NSWorkspace sharedWorkspace] launchApplication:kChromeInstallPath]; |
303 } | 378 } |
304 } | 379 } |
OLD | NEW |