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

Unified Diff: chrome/installer/gcapi_mac/gcapi.mm

Issue 10834102: mac: Let gcapi install chrome as the CGSession owner (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 8 years, 4 months 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/installer/gcapi_mac/gcapi.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/installer/gcapi_mac/gcapi.mm
diff --git a/chrome/installer/gcapi_mac/gcapi.mm b/chrome/installer/gcapi_mac/gcapi.mm
index ab561e7977c9c67ca94a980c710e23da0971b09a..7c9396124df768778a1539c84eead6ec8618f69e 100644
--- a/chrome/installer/gcapi_mac/gcapi.mm
+++ b/chrome/installer/gcapi_mac/gcapi.mm
@@ -5,30 +5,33 @@
#include "chrome/installer/gcapi_mac/gcapi.h"
#import <Cocoa/Cocoa.h>
+#include <grp.h>
+#include <pwd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
namespace {
+// The "~~" prefixes are replaced with the home directory of the
+// console owner (i.e. not the home directory of the euid).
NSString* const kChromeInstallPath = @"/Applications/Google Chrome.app";
NSString* const kBrandKey = @"KSBrandID";
-NSString* const kSystemBrandPath = @"/Library/Google/Google Chrome Brand.plist";
-NSString* const kUserBrandPath = @"~/Library/Google/Google Chrome Brand.plist";
+NSString* const kUserBrandPath = @"~~/Library/Google/Google Chrome Brand.plist";
NSString* const kSystemKsadminPath =
@"/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/"
"Contents/MacOS/ksadmin";
NSString* const kUserKsadminPath =
- @"~/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/"
+ @"~~/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/"
"Contents/MacOS/ksadmin";
NSString* const kSystemMasterPrefsPath =
@"/Library/Google/Google Chrome Master Preferences";
NSString* const kUserMasterPrefsPath =
- @"~/Library/Application Support/Google/Google Chrome Master Preferences";
+ @"~~/Library/Application Support/Google/Google Chrome Master Preferences";
NSString* const kChannelKey = @"KSChannelID";
NSString* const kVersionKey = @"KSVersion";
@@ -60,26 +63,58 @@ bool IsOSXVersionSupported() {
return mac_os_x_minor_version >= 6 && mac_os_x_minor_version <= 8;
}
+// Returns the pid/gid of the logged-in user, even if getuid() claims that the
+// current user is root.
+// Returns NULL on error.
+passwd* GetRealUserId() {
+ CFDictionaryRef session_info_dict = CGSessionCopyCurrentDictionary();
+ [NSMakeCollectable(session_info_dict) autorelease];
+ if (!session_info_dict)
+ return NULL; // Possibly no screen plugged in.
+
+ CFNumberRef ns_uid = (CFNumberRef)CFDictionaryGetValue(session_info_dict,
+ kCGSessionUserIDKey);
+ if (CFGetTypeID(ns_uid) != CFNumberGetTypeID())
+ return NULL;
+
+ uid_t uid;
+ BOOL success = CFNumberGetValue(ns_uid, kCFNumberSInt32Type, &uid);
+ if (!success)
+ return NULL;
+
+ return getpwuid(uid);
+}
+
enum TicketKind {
kSystemTicket, kUserTicket
};
-BOOL HasChromeTicket(TicketKind kind) {
+// Replaces "~~" with |home_dir|.
+NSString* AdjustHomedir(NSString* s, const char* home_dir) {
+ if (![s hasPrefix:@"~~"])
+ return s;
+ NSString* ns_home_dir = [NSString stringWithUTF8String:home_dir];
+ return [ns_home_dir stringByAppendingString:[s substringFromIndex:2]];
+}
+
+BOOL HasChromeTicket(TicketKind kind, const passwd* user) {
// Don't use Objective-C 2 loop syntax, in case an installer runs on 10.4.
- NSMutableArray* keystonePaths =
+ NSMutableArray* keystone_paths =
[NSMutableArray arrayWithObject:kSystemKsadminPath];
- if (kind == kUserTicket && geteuid() != 0)
- [keystonePaths insertObject:kUserKsadminPath atIndex:0];
- NSEnumerator* e = [keystonePaths objectEnumerator];
- id ksPath;
- while ((ksPath = [e nextObject])) {
+ if (kind == kUserTicket) {
+ [keystone_paths insertObject:AdjustHomedir(kUserKsadminPath, user->pw_dir)
+ atIndex:0];
+ }
+ NSEnumerator* e = [keystone_paths objectEnumerator];
+ id ks_path;
+ while ((ks_path = [e nextObject])) {
NSTask* task = nil;
NSString* string = nil;
- bool ksadminRanSuccessfully = false;
+ bool ksadmin_ran_successfully = false;
@try {
task = [[NSTask alloc] init];
- [task setLaunchPath:ksPath];
+ [task setLaunchPath:ks_path];
NSArray* arguments = @[
kind == kUserTicket ? @"--user-store" : @"--system-store",
@@ -87,6 +122,12 @@ BOOL HasChromeTicket(TicketKind kind) {
@"--productid",
@"com.google.Chrome",
];
+ if (geteuid() == 0 && kind == kUserTicket) {
+ NSString* run_as = [NSString stringWithUTF8String:user->pw_name];
+ [task setLaunchPath:@"/usr/bin/sudo"];
+ arguments = [@[@"-u", run_as, ks_path]
+ arrayByAddingObjectsFromArray:arguments];
+ }
[task setArguments:arguments];
NSPipe* pipe = [NSPipe pipe];
@@ -99,28 +140,27 @@ BOOL HasChromeTicket(TicketKind kind) {
NSData* data = [file readDataToEndOfFile];
[task waitUntilExit];
- ksadminRanSuccessfully = [task terminationStatus] == 0;
+ ksadmin_ran_successfully = [task terminationStatus] == 0;
string = [[[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding] autorelease];
}
@catch (id exception) {
- // Most likely, ksPath didn't exist.
+ // Most likely, ks_path didn't exist.
}
[task release];
- if (ksadminRanSuccessfully && [string length] > 0)
+ if (ksadmin_ran_successfully && [string length] > 0)
return YES;
}
return NO;
}
-// Returns the file permission mask for files created by gcapi.
-mode_t Permissions() {
- return 0755;
-}
+// File permission mask for files created by gcapi.
+const mode_t kUserPermissions = 0755;
+const mode_t kAdminPermissions = 0775;
-BOOL CreatePathToFile(NSString* path) {
+BOOL CreatePathToFile(NSString* path, const passwd* user) {
path = [path stringByDeletingLastPathComponent];
// Default owner, group, permissions:
@@ -128,14 +168,21 @@ BOOL CreatePathToFile(NSString* path) {
// more information, see umask.
// * The owner ID is set to the effective user ID of the process.
// * The group ID is set to that of the parent directory.
- // The default group ID is fine. Owner ID is fine too, since user directory
- // paths won't be created if euid is 0. Do set permissions explicitly; for
- // admin paths all admins can write, for user paths just the owner may.
- NSMutableDictionary* attributes = [NSMutableDictionary
- dictionaryWithObject:[NSNumber numberWithShort:Permissions()]
- forKey:NSFilePosixPermissions];
- if (geteuid() == 0)
- [attributes setObject:@"wheel" forKey:NSFileGroupOwnerAccountName];
+ // The default group ID is fine. Owner ID is fine if creating a system path,
+ // but when creating a user path explicitly set the owner in case euid is 0.
+ // Do set permissions explicitly; for admin paths all admins can write, for
+ // user paths just the owner may.
+ NSMutableDictionary* attributes = [NSMutableDictionary dictionary];
+ if (user) {
+ [attributes setObject:[NSNumber numberWithShort:kUserPermissions]
+ forKey:NSFilePosixPermissions];
+ [attributes setObject:[NSNumber numberWithInt:user->pw_uid]
+ forKey:NSFileOwnerAccountID];
+ } else {
+ [attributes setObject:[NSNumber numberWithShort:kAdminPermissions]
+ forKey:NSFilePosixPermissions];
+ [attributes setObject:@"admin" forKey:NSFileGroupOwnerAccountName];
+ }
NSFileManager* manager = [NSFileManager defaultManager];
return [manager createDirectoryAtPath:path
@@ -144,33 +191,42 @@ BOOL CreatePathToFile(NSString* path) {
error:nil];
}
-// Tries to write |data| at |system_path| or if that fails and geteuid() is not
-// 0 at |user_path|. Returns the path where it wrote, or nil on failure.
-NSString* WriteData(NSData* data, NSString* system_path, NSString* user_path) {
+// Tries to write |data| at |user_path|.
+// Returns the path where it wrote, or nil on failure.
+NSString* WriteUserData(NSData* data,
+ NSString* user_path,
+ const passwd* user) {
+ user_path = AdjustHomedir(user_path, user->pw_dir);
+ if (CreatePathToFile(user_path, user) &&
+ [data writeToFile:user_path atomically:YES]) {
+ chmod([user_path fileSystemRepresentation], kUserPermissions & ~0111);
+ chown([user_path fileSystemRepresentation], user->pw_uid, user->pw_gid);
+ return user_path;
+ }
+ return nil;
+}
+
+// Tries to write |data| at |system_path| or if that fails at |user_path|.
+// Returns the path where it wrote, or nil on failure.
+NSString* WriteData(NSData* data,
+ NSString* system_path,
+ NSString* user_path,
+ const passwd* user) {
// Try system first.
- if (CreatePathToFile(system_path) &&
+ if (CreatePathToFile(system_path, NULL) &&
[data writeToFile:system_path atomically:YES]) {
- // files are created with group of parent dir (good), owner of euid (good).
- chmod([system_path fileSystemRepresentation], Permissions() & ~0111);
+ chmod([system_path fileSystemRepresentation], kAdminPermissions & ~0111);
+ // Make sure the file is owned by group admin.
+ if (group* group = getgrnam("admin"))
+ chown([system_path fileSystemRepresentation], 0, group->gr_gid);
return system_path;
}
// Failed, try user.
- // -stringByExpandingTildeInPath returns root's home directory if this is run
- // setuid root, and in that case the kSystemBrandPath path above should have
- // worked anyway. So only try user if geteuid() isn't root.
- if (geteuid() != 0) {
- user_path = [user_path stringByExpandingTildeInPath];
- if (CreatePathToFile(user_path) &&
- [data writeToFile:user_path atomically:YES]) {
- chmod([user_path fileSystemRepresentation], Permissions() & ~0111);
- return user_path;
- }
- }
- return nil;
+ return WriteUserData(data, user_path, user);
}
-NSString* WriteBrandCode(const char* brand_code) {
+NSString* WriteBrandCode(const char* brand_code, const passwd* user) {
NSDictionary* brand_dict = @{
kBrandKey: [NSString stringWithUTF8String:brand_code],
};
@@ -179,15 +235,16 @@ NSString* WriteBrandCode(const char* brand_code) {
format:NSPropertyListBinaryFormat_v1_0
errorDescription:nil];
- return WriteData(contents, kSystemBrandPath, kUserBrandPath);
+ return WriteUserData(contents, kUserBrandPath, user);
}
BOOL WriteMasterPrefs(const char* master_prefs_contents,
- size_t master_prefs_contents_size) {
+ size_t master_prefs_contents_size,
+ const passwd* user) {
NSData* contents = [NSData dataWithBytes:master_prefs_contents
length:master_prefs_contents_size];
- return
- WriteData(contents, kSystemMasterPrefsPath, kUserMasterPrefsPath) != nil;
+ return WriteData(
+ contents, kSystemMasterPrefsPath, kUserMasterPrefsPath, user) != nil;
}
NSString* PathToFramework(NSString* app_path, NSDictionary* info_plist) {
@@ -214,27 +271,26 @@ bool isbrandchar(int c) {
int GoogleChromeCompatibilityCheck(unsigned* reasons) {
unsigned local_reasons = 0;
+ @autoreleasepool {
+ passwd* user = GetRealUserId();
- NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
-
- if (!IsOSXVersionSupported())
- local_reasons |= GCCC_ERROR_OSNOTSUPPORTED;
-
- if (HasChromeTicket(kSystemTicket))
- local_reasons |= GCCC_ERROR_SYSTEMLEVELALREADYPRESENT;
+ if (!IsOSXVersionSupported())
+ local_reasons |= GCCC_ERROR_OSNOTSUPPORTED;
- if (geteuid() != 0 && HasChromeTicket(kUserTicket))
- local_reasons |= GCCC_ERROR_USERLEVELALREADYPRESENT;
+ if (HasChromeTicket(kSystemTicket, NULL))
+ local_reasons |= GCCC_ERROR_ALREADYPRESENT;
- if (![[NSFileManager defaultManager] isWritableFileAtPath:@"/Applications"])
- local_reasons |= GCCC_ERROR_ACCESSDENIED;
+ if (HasChromeTicket(kUserTicket, user))
+ local_reasons |= GCCC_ERROR_ALREADYPRESENT;
- [pool drain];
+ if (![[NSFileManager defaultManager] isWritableFileAtPath:@"/Applications"])
+ local_reasons |= GCCC_ERROR_ACCESSDENIED;
- // Done. Copy/return results.
+ if ([[NSFileManager defaultManager] fileExistsAtPath:kChromeInstallPath])
+ local_reasons |= GCCC_ERROR_ALREADYPRESENT;
+ }
if (reasons != NULL)
*reasons = local_reasons;
-
return local_reasons == 0;
}
@@ -246,6 +302,8 @@ int InstallGoogleChrome(const char* source_path,
return 0;
@autoreleasepool {
+ passwd* user = GetRealUserId();
+
NSString* app_path = [NSString stringWithUTF8String:source_path];
NSString* info_plist_path =
[app_path stringByAppendingPathComponent:@"Contents/Info.plist"];
@@ -260,9 +318,13 @@ int InstallGoogleChrome(const char* source_path,
}
@try {
+ // install.sh tries to make the installed app admin-writable, but
+ // only when it's not run as root.
+ NSString* run_as = [NSString stringWithUTF8String:user->pw_name];
NSTask* task = [[[NSTask alloc] init] autorelease];
- [task setLaunchPath:install_script];
- [task setArguments:@[app_path, kChromeInstallPath]];
+ [task setLaunchPath:@"/usr/bin/sudo"];
+ [task setArguments:
+ @[@"-u", run_as, install_script, app_path, kChromeInstallPath]];
[task launch];
[task waitUntilExit];
if ([task terminationStatus] != 0) {
@@ -285,20 +347,21 @@ int InstallGoogleChrome(const char* source_path,
NSString* brand_path = nil;
if (valid_brand_code)
- brand_path = WriteBrandCode(brand_code);
+ brand_path = WriteBrandCode(brand_code, user);
// Write master prefs.
if (master_prefs_contents)
- WriteMasterPrefs(master_prefs_contents, master_prefs_contents_size);
+ WriteMasterPrefs(master_prefs_contents, master_prefs_contents_size, user);
- // TODO Set default browser if requested. Will be tricky when running as
- // root.
+ // TODO Set default browser if requested.
}
return 1;
}
int LaunchGoogleChrome() {
@autoreleasepool {
+ // NSWorkspace launches processes as the current console owner,
+ // even when running with euid of 0.
return [[NSWorkspace sharedWorkspace] launchApplication:kChromeInstallPath];
}
}
« no previous file with comments | « chrome/installer/gcapi_mac/gcapi.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698