summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/cocoa/keystone_glue.h11
-rw-r--r--chrome/browser/cocoa/keystone_glue.mm157
-rwxr-xr-xchrome/browser/cocoa/keystone_promote_preflight.sh42
-rwxr-xr-xchrome/tools/build/mac/keystone_install.sh64
4 files changed, 265 insertions, 9 deletions
diff --git a/chrome/browser/cocoa/keystone_glue.h b/chrome/browser/cocoa/keystone_glue.h
index 81d74a3..6ae4c1f 100644
--- a/chrome/browser/cocoa/keystone_glue.h
+++ b/chrome/browser/cocoa/keystone_glue.h
@@ -43,6 +43,16 @@ extern const NSString* const kAutoupdateStatusNotification;
extern const NSString* const kAutoupdateStatusStatus;
extern const NSString* const kAutoupdateStatusVersion;
+namespace {
+ enum BrandFileType {
+ kBrandFileTypeNotDetermined = 0,
+ kBrandFileTypeNone,
+ kBrandFileTypeUser,
+ kBrandFileTypeSystem,
+ };
+
+} // namespace
+
// KeystoneGlue is an adapter around the KSRegistration class, allowing it to
// be used without linking directly against its containing KeystoneRegistration
// framework. This is used in an environment where most builds (such as
@@ -66,6 +76,7 @@ extern const NSString* const kAutoupdateStatusVersion;
NSString* url_;
NSString* version_;
NSString* channel_; // Logically: Dev, Beta, or Stable.
+ BrandFileType brandFileType_;
// And the Keystone registration itself, with the active timer
KSRegistration* registration_; // strong
diff --git a/chrome/browser/cocoa/keystone_glue.mm b/chrome/browser/cocoa/keystone_glue.mm
index 468eca90..5cb125a 100644
--- a/chrome/browser/cocoa/keystone_glue.mm
+++ b/chrome/browser/cocoa/keystone_glue.mm
@@ -40,6 +40,8 @@ NSString* KSRegistrationPreserveTrustedTesterTokenKey = @"PreserveTTT";
NSString* KSRegistrationTagKey = @"Tag";
NSString* KSRegistrationTagPathKey = @"TagPath";
NSString* KSRegistrationTagKeyKey = @"TagKey";
+NSString* KSRegistrationBrandPathKey = @"BrandPath";
+NSString* KSRegistrationBrandKeyKey = @"BrandKey";
NSString *KSRegistrationDidCompleteNotification =
@"KSRegistrationDidCompleteNotification";
@@ -59,6 +61,30 @@ NSString *KSUpdateCheckSuccessfullyInstalledKey = @"SuccessfullyInstalled";
NSString *KSRegistrationRemoveExistingTag = @"";
#define KSRegistrationPreserveExistingTag nil
+// Constants for the brand file (uses an external file so it can survive updates
+// to Chrome.
+
+#if defined(GOOGLE_CHROME_BUILD)
+#define kBrandFileName @"Google Chrome Brand.plist";
+#elif defined(CHROMIUM_BUILD)
+#define kBrandFileName @"Chromium Brand.plist";
+#else
+#error Unknown branding
+#endif
+
+// These directories are hardcoded in Keystone promotion preflight and the
+// Keystone install script, so NSSearchPathForDirectoriesInDomains isn't used
+// since the scripts couldn't use anything like that.
+NSString* kBrandUserFile = @"~/Library/Google/" kBrandFileName;
+NSString* kBrandSystemFile = @"/Library/Google/" kBrandFileName;
+
+NSString* UserBrandFilePath() {
+ return [kBrandUserFile stringByStandardizingPath];
+}
+NSString* SystemBrandFilePath() {
+ return [kBrandSystemFile stringByStandardizingPath];
+}
+
} // namespace
@interface KSRegistration : NSObject
@@ -144,6 +170,9 @@ NSString *KSRegistrationRemoveExistingTag = @"";
- (void)changePermissionsForPromotionWithTool:(NSString*)toolPath;
- (void)changePermissionsForPromotionComplete;
+// Returns the brand file path to use for Keystone.
+- (NSString*)brandFilePath;
+
@end // @interface KeystoneGlue(Private)
const NSString* const kAutoupdateStatusNotification =
@@ -154,6 +183,7 @@ const NSString* const kAutoupdateStatusVersion = @"version";
namespace {
const NSString* const kChannelKey = @"KSChannelID";
+const NSString* const kBrandKey = @"KSBrandID";
} // namespace
@@ -258,6 +288,88 @@ const NSString* const kChannelKey = @"KSChannelID";
channel_ = [channel retain];
}
+- (NSString*)brandFilePath {
+ DCHECK(version_ != nil) << "-loadParameters must be called first";
+
+ if (brandFileType_ == kBrandFileTypeNotDetermined) {
+
+ // Default to none.
+ brandFileType_ = kBrandFileTypeNone;
+
+ // Having a channel means Dev/Beta, so there is no brand code to go with
+ // those.
+ if ([channel_ length] == 0) {
+
+ NSString* userBrandFile = UserBrandFilePath();
+ NSString* systemBrandFile = SystemBrandFilePath();
+
+ NSFileManager* fm = [NSFileManager defaultManager];
+
+ // If there is a system brand file, use it.
+ if ([fm fileExistsAtPath:systemBrandFile]) {
+ // System
+
+ // Use the system file that is there.
+ brandFileType_ = kBrandFileTypeSystem;
+
+ // Clean up any old user level file.
+ if ([fm fileExistsAtPath:userBrandFile]) {
+ [fm removeItemAtPath:userBrandFile error:NULL];
+ }
+
+ } else {
+ // User
+
+ NSDictionary* infoDictionary = [self infoDictionary];
+ NSString* appBundleBrandID = [infoDictionary objectForKey:kBrandKey];
+
+ NSString* storedBrandID = nil;
+ if ([fm fileExistsAtPath:userBrandFile]) {
+ NSDictionary* storedBrandDict =
+ [NSDictionary dictionaryWithContentsOfFile:userBrandFile];
+ storedBrandID = [storedBrandDict objectForKey:kBrandKey];
+ }
+
+ if ((appBundleBrandID != nil) &&
+ (![storedBrandID isEqualTo:appBundleBrandID])) {
+ // App and store don't match, update store and use it.
+ NSDictionary* storedBrandDict =
+ [NSDictionary dictionaryWithObject:appBundleBrandID
+ forKey:kBrandKey];
+ if ([storedBrandDict writeToFile:userBrandFile atomically:YES]) {
+ brandFileType_ = kBrandFileTypeUser;
+ }
+ } else if (storedBrandID) {
+ // Had stored brand, use it.
+ brandFileType_ = kBrandFileTypeUser;
+ }
+ }
+ }
+
+ }
+
+ NSString* result = nil;
+ switch (brandFileType_) {
+ case kBrandFileTypeUser:
+ result = UserBrandFilePath();
+ break;
+
+ case kBrandFileTypeSystem:
+ result = SystemBrandFilePath();
+ break;
+
+ case kBrandFileTypeNotDetermined:
+ NOTIMPLEMENTED();
+ // Fall through
+ case kBrandFileTypeNone:
+ // Clear the value.
+ result = @"";
+ break;
+
+ }
+ return result;
+}
+
- (BOOL)loadKeystoneRegistration {
if (!productID_ || !appPath_ || !url_ || !version_)
return NO;
@@ -292,6 +404,15 @@ const NSString* const kChannelKey = @"KSChannelID";
NSNumber* preserveTTToken = [NSNumber numberWithBool:YES];
NSString* tagPath = [self appInfoPlistPath];
+ NSString* brandKey = kBrandKey;
+ NSString* brandPath = [self brandFilePath];
+
+ if ([brandPath length] == 0) {
+ // Brand path and brand key must be cleared together or ksadmin seems
+ // to throw an error.
+ brandKey = @"";
+ }
+
return [NSDictionary dictionaryWithObjectsAndKeys:
version_, KSRegistrationVersionKey,
xcType, KSRegistrationExistenceCheckerTypeKey,
@@ -301,6 +422,8 @@ const NSString* const kChannelKey = @"KSChannelID";
channel_, KSRegistrationTagKey,
tagPath, KSRegistrationTagPathKey,
kChannelKey, KSRegistrationTagKeyKey,
+ brandPath, KSRegistrationBrandPathKey,
+ brandKey, KSRegistrationBrandKeyKey,
nil];
}
@@ -632,11 +755,15 @@ const NSString* const kChannelKey = @"KSChannelID";
// TODO(mark): Remove when able!
//
- // keystone_promote_preflight is hopefully temporary. It's here to ensure
- // that the Keystone system ticket store is in a usable state for all users
- // on the system. Ideally, Keystone's installer or another part of Keystone
- // would handle this. The underlying problem is http://b/2285921, and it
- // causes http://b/2289908, which this workaround addresses.
+ // keystone_promote_preflight will copy the current brand information out to
+ // the system level so all users can share the data as part of the ticket
+ // promotion.
+ //
+ // It will also ensure that the Keystone system ticket store is in a usable
+ // state for all users on the system. Ideally, Keystone's installer or
+ // another part of Keystone would handle this. The underlying problem is
+ // http://b/2285921, and it causes http://b/2289908, which this workaround
+ // addresses.
//
// This is run synchronously, which isn't optimal, but
// -[KSRegistration promoteWithParameters:authorization:] is currently
@@ -650,7 +777,14 @@ const NSString* const kChannelKey = @"KSChannelID";
[mac_util::MainAppBundle() pathForResource:@"keystone_promote_preflight"
ofType:@"sh"];
const char* preflightPathC = [preflightPath fileSystemRepresentation];
- const char* arguments[] = {NULL};
+ const char* userBrandFile = NULL;
+ const char* systemBrandFile = NULL;
+ if (brandFileType_ == kBrandFileTypeUser) {
+ // Running with user level brand file, promote to the system level.
+ userBrandFile = [UserBrandFilePath() fileSystemRepresentation];
+ systemBrandFile = [SystemBrandFilePath() fileSystemRepresentation];
+ }
+ const char* arguments[] = {userBrandFile, systemBrandFile, NULL};
int exit_status;
status = authorization_util::ExecuteWithPrivilegesAndWait(
@@ -678,6 +812,17 @@ const NSString* const kChannelKey = @"KSChannelID";
authorization_.swap(authorization);
NSDictionary* parameters = [self keystoneParameters];
+
+ // If the brand file is user level, update parameters to point to the new
+ // system level file during promotion.
+ if (brandFileType_ == kBrandFileTypeUser) {
+ NSMutableDictionary* temp_parameters =
+ [[parameters mutableCopy] autorelease];
+ [temp_parameters setObject:SystemBrandFilePath()
+ forKey:KSRegistrationBrandPathKey];
+ parameters = temp_parameters;
+ }
+
if (![registration_ promoteWithParameters:parameters
authorization:authorization_]) {
[self updateStatus:kAutoupdatePromoteFailed version:nil];
diff --git a/chrome/browser/cocoa/keystone_promote_preflight.sh b/chrome/browser/cocoa/keystone_promote_preflight.sh
index d1c4c59..4bf31e8 100755
--- a/chrome/browser/cocoa/keystone_promote_preflight.sh
+++ b/chrome/browser/cocoa/keystone_promote_preflight.sh
@@ -8,6 +8,10 @@
# environment for Keystone installation. Ultimately, these features should be
# integrated directly into the Keystone installation.
#
+# If the two branding paths are given, then the branding information is also
+# copied and the permissions on the system branding file are set to be owned by
+# root, but readable by anyone.
+#
# Note that this script will be invoked with the real user ID set to the
# user's ID, but the effective user ID set to 0 (root). bash -p is used on
# the first line to prevent bash from setting the effective user ID to the
@@ -25,11 +29,45 @@ export PATH="/usr/bin:/usr/sbin:/bin:/sbin"
# chrome/browser/cocoa/authorization_util.h.
echo "${$}"
-if [ ${#} -ne 0 ] ; then
- echo "usage: ${0}" >& 2
+if [ ${#} -ne 0 ] && [ ${#} -ne 2 ] ; then
+ echo "usage: ${0} [USER_BRAND SYSTEM_BRAND]" >& 2
exit 2
fi
+if [ ${#} -eq 2 ] ; then
+ USER_BRAND="${1}"
+ SYSTEM_BRAND="${2}"
+
+ # Make sure that USER_BRAND is an absolute path and that it exists.
+ if [ -z "${USER_BRAND}" ] || \
+ [ "${USER_BRAND:0:1}" != "/" ] || \
+ [ ! -f "${USER_BRAND}" ] ; then
+ echo "${0}: must provide an absolute path naming an existing user file" >& 2
+ exit 3
+ fi
+
+ # Make sure that SYSTEM_BRAND is an absolute path.
+ if [ -z "${SYSTEM_BRAND}" ] || [ "${SYSTEM_BRAND:0:1}" != "/" ] ; then
+ echo "${0}: must provide an absolute path naming a system file" >& 2
+ exit 4
+ fi
+
+ # Make sure the directory for the system brand file exists.
+ SYSTEM_BRAND_DIR=$(dirname "${SYSTEM_BRAND}")
+ if [ ! -e "${SYSTEM_BRAND_DIR}" ] ; then
+ mkdir -p "${SYSTEM_BRAND_DIR}"
+ # Permissions on this directory will be fixed up at the end of this script.
+ fi
+
+ # Copy the brand file
+ cp "${USER_BRAND}" "${SYSTEM_BRAND}" >& /dev/null
+
+ # Ensure the right ownership and permissions
+ chown "root:wheel" "${SYSTEM_BRAND}" >& /dev/null
+ chmod "a+r,u+w,go-w" "${SYSTEM_BRAND}" >& /dev/null
+
+fi
+
OWNER_GROUP="root:admin"
CHMOD_MODE="a+rX,u+w,go-w"
diff --git a/chrome/tools/build/mac/keystone_install.sh b/chrome/tools/build/mac/keystone_install.sh
index 67e3a78..73f0920 100755
--- a/chrome/tools/build/mac/keystone_install.sh
+++ b/chrome/tools/build/mac/keystone_install.sh
@@ -196,6 +196,17 @@ function ksadmin_supports_tagpath_tagkey() {
# return value.
}
+# Returns 0 (true) if ksadmin supports --tag-path, --tag-key, --brand-path,
+# and --brand-key.
+function ksadmin_supports_brandpath_brandkey() {
+ # --brand-path and --brand-key were introduced in Keystone 1.0.8.1620.
+ # --tag-path and --tag-key are already supported if the brand arguments are
+ # also supported.
+ is_ksadmin_version_ge 1.0.8.1620
+ # The return value of is_ksadmin_version_ge is used as this function's
+ # return value.
+}
+
# The argument should be the disk image path. Make sure it exists.
if [ $# -lt 1 ] || [ ! -d "${1}" ]; then
exit 2
@@ -322,6 +333,11 @@ if [ ${EUID} -ne 0 ] ; then
set -e
fi
+# Collect the current app brand, it will be use later.
+BRAND_ID_KEY=KSBrandID
+APP_BRAND=$(defaults read "${DEST}/Contents/Info" "${BRAND_ID_KEY}" 2>/dev/null ||
+ true)
+
# Don't use rsync -a, because -a expands to -rlptgoD. -g and -o copy owners
# and groups, respectively, from the source, and that is undesirable in this
# case. -D copies devices and special files; copying devices only works
@@ -420,8 +436,54 @@ fi
# beforehand.
ksadmin_version >& /dev/null || true
+# The brand information is stored differently depending on whether this is
+# running for a system or user ticket.
+BRAND_PATH_PLIST=
+SET_BRAND_FILE_ACCESS=no
+if [ ${EUID} -ne 0 ] ; then
+ # Using a user level ticket.
+ BRAND_PATH_PLIST=~/"Library/Google/Google Chrome Brand"
+else
+ # Using a system level ticket.
+ BRAND_PATH_PLIST="/Library/Google/Google Chrome Brand"
+ SET_BRAND_FILE_ACCESS=yes
+fi
+# If the user manually updated their copy of Chrome, there might be new brand
+# information in the app bundle, and that needs to be copied out into the
+# file Keystone looks at.
+BRAND_PATH="${BRAND_PATH_PLIST}.plist"
+if [ -n "${APP_BRAND}" ] ; then
+ BRAND_PATH_DIR=$(dirname "${BRAND_PATH}")
+ if [ ! -e "${BRAND_PATH_DIR}" ] ; then
+ mkdir -p "${BRAND_PATH_DIR}"
+ fi
+ defaults write "${BRAND_PATH_PLIST}" "${BRAND_ID_KEY}" -string "${APP_BRAND}"
+ if [ "${SET_BRAND_FILE_ACCESS}" = "yes" ] ; then
+ chown "root:wheel" "${BRAND_PATH}" >& /dev/null
+ chmod "a+r,u+w,go-w" "${BRAND_PATH}" >& /dev/null
+ fi
+fi
+# Confirm that the brand file exists (it is optional)
+if [ ! -f "${BRAND_PATH}" ] ; then
+ BRAND_PATH=
+ # ksadmin reports an error if brand-path is cleared but brand-key still has a
+ # value, so if there is no path, clear the key also.
+ BRAND_ID_KEY=
+fi
+
# Notify Keystone.
-if ksadmin_supports_tagpath_tagkey ; then
+if ksadmin_supports_brandpath_brandkey ; then
+ ksadmin --register \
+ -P "${PRODUCT_ID}" \
+ --version "${NEW_VERSION_KS}" \
+ --xcpath "${DEST}" \
+ --url "${URL}" \
+ --tag "${CHANNEL_ID}" \
+ --tag-path "${DEST}/Contents/Info.plist" \
+ --tag-key "${CHANNEL_ID_KEY}" \
+ --brand-path "${BRAND_PATH}" \
+ --brand-key "${BRAND_ID_KEY}" || exit 11
+elif ksadmin_supports_tagpath_tagkey ; then
ksadmin --register \
-P "${PRODUCT_ID}" \
--version "${NEW_VERSION_KS}" \