Commit 91e28fcd authored by kerrnel's avatar kerrnel Committed by Commit bot

Do Keychain reauthorization at update time

In addition to the at-launch reauthorization, this adds an at-update
reauthorization step. It only runs for users not on a system Keystone ticket,
because the updater runs as root when on a system ticket, and root can't read
individual user's Keychains. The at-update reauthorization is intended to
handle the reauthorization for users who rarely restart Chrome and might miss
out on the at-launch step during the window where Chrome is signed by the old
certificate but has the new reauthorization code in place. The at-update
reauthorization step can remain in place even after the certificate switch
occurs by shipping an old reauthorization stub binary signed with the old
certificate.

BUG=629906
NOPRESUBMIT=true

Review-Url: https://codereview.chromium.org/2745223006
Cr-Commit-Position: refs/heads/master@{#457530}
parent 172041b1
......@@ -21,5 +21,8 @@ ___asan_default_options
# Entry point from the app mode loader.
_ChromeAppModeStart_v4
# Entry point for the keychain reauthorization stub.
_KeychainReauthorizeIfNeededAtUpdate
# _ChromeMain must be listed last. That's the whole point of this file.
_ChromeMain
......@@ -13,6 +13,8 @@ class NSString;
namespace chrome {
extern "C" {
// Reauthorizes the keychain entry, but only if it's determined that it's
// necessary. pref_key is looked up in the system's standard user defaults
// (preferences) and it is associated with both the number of previous attempts,
......@@ -29,6 +31,15 @@ namespace chrome {
// product than it is to any specific profile (--user-data-dir).
void KeychainReauthorizeIfNeeded(NSString* pref_key, int max_tries);
// A wrapper to call KeychainReauthorizeIfNeeded under a local autorelease pool.
// This is used to call the function from the keychain_reauthorization binary in
// the keystone_install.sh script.
__attribute__((visibility("default"))) void KeychainReauthorizeIfNeededAtUpdate(
NSString* pref_key,
int max_tries);
} // extern "C"
} // namespace chrome
#endif // CHROME_BROWSER_MAC_KEYCHAIN_REAUTHORIZE_H_
......@@ -156,7 +156,6 @@ void KeychainReauthorizeIfNeeded(NSString* pref_key, int max_tries) {
[user_defaults synchronize];
bool success = KeychainReauthorize();
if (!success)
return;
......@@ -173,4 +172,10 @@ void KeychainReauthorizeIfNeeded(NSString* pref_key, int max_tries) {
}
}
void KeychainReauthorizeIfNeededAtUpdate(NSString* pref_key, int max_tries) {
@autoreleasepool {
KeychainReauthorizeIfNeeded(pref_key, max_tries);
}
}
} // namespace chrome
......@@ -49,6 +49,7 @@ copy("copies") {
visibility = [ ":mac" ]
deps = [
"//chrome/installer/mac:keychain_reauthorize",
"//chrome/installer/mac/third_party/bsdiff:goobsdiff",
"//chrome/installer/mac/third_party/bsdiff:goobspatch",
"//chrome/installer/mac/third_party/xz:lzma_decompress",
......@@ -59,6 +60,7 @@ copy("copies") {
sources = [
"$root_out_dir/goobsdiff",
"$root_out_dir/goobspatch",
"$root_out_dir/keychain_reauthorize",
"$root_out_dir/liblzma_decompress.dylib",
"$root_out_dir/xz",
"$root_out_dir/xzdec",
......@@ -92,3 +94,10 @@ copy("copies") {
"$_packaging_dir/{{source_file_part}}",
]
}
executable("keychain_reauthorize") {
sources = [
"keychain_reauthorize_main.cc",
]
libs = [ "CoreFoundation.framework" ]
}
......@@ -243,6 +243,18 @@ make_patch_fs() {
exit 13
fi
local patch_keychain_reauthorize_dir="${patch_fs}/.keychain_reauthorize"
if ! mkdir "${patch_keychain_reauthorize_dir}"; then
err "could not mkdir patch_keychain_reauthorize_dir"
exit 13
fi
if ! cp -p "${SCRIPT_DIR}/.keychain_reauthorize/${old_app_bundleid}"
"${patch_keychain_reauthorize_dir}/${old_app_bundleid}"; then
err "could not copy keychain_reauthorize"
exit 13
fi
local patch_dotpatch_dir="${patch_fs}/.patch"
if ! mkdir "${patch_dotpatch_dir}"; then
err "could not mkdir patch_dotpatch_dir"
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The entry point for the Mac Chrome Keychain Reauthorization process,
// which runs at update time. It needs to be signed by the old certificate
// in order to have access to the existing Keychain items, so it takes the
// form of this little stub that uses dlopen and dlsym to find a current
// Chrome framework, which can be signed by any certificate including the new
// one. This architecture allows the updater to peform keychain
// reauthorization by using an old copy of this executable signed with the old
// certificate even after the rest of Chrome has switched to being signed with
// the new certificate. The reauthorization dylib remains in the framework to
// avoid duplication and to allow it to change over time without having to
// re-sign this executable with the old certificate. This uses dlopen and
// dlsym to avoid problems linking with a library whose path is not fixed and
// whose version changes with each release.
//
// In order to satisfy the requirements of items stored in the Keychain, this
// executable needs to be named "com.google.Chrome", or
// "com.google.Chrome.canary", because the original applications were signed
// with designated requirements requiring the identifier to be one of those
// names.
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <CoreFoundation/CoreFoundation.h>
__attribute__((visibility("default"))) int main(int argc, char* argv[]) {
const char* me = argv[0];
if (argc != 2) {
fprintf(stderr, "usage: %s <framework_dylib_path>\n", me);
return 1;
}
const char* framework_dylib_path = argv[1];
void* framework_dylib = dlopen(framework_dylib_path, RTLD_LAZY | RTLD_GLOBAL);
if (!framework_dylib) {
fprintf(stderr, "%s: dlopen: %s\n", me, dlerror());
return 1;
}
CFStringRef keychain_reauthorize_pref =
CFSTR("KeychainReauthorizeInAppSpring2017");
const int keychain_reauthorize_max_tries = 3;
typedef void (*KeychainReauthorizeIfNeeded)(CFStringRef, int);
KeychainReauthorizeIfNeeded keychain_reauth =
reinterpret_cast<KeychainReauthorizeIfNeeded>(
dlsym(framework_dylib, "KeychainReauthorizeIfNeededAtUpdate"));
if (!keychain_reauth) {
fprintf(stderr, "%s: dlsym: %s\n", me, dlerror());
return 1;
}
keychain_reauth(keychain_reauthorize_pref, keychain_reauthorize_max_tries);
// As in chrome_exe_main_mac.cc: exit, don't return from main, to avoid the
// apparent removal of main from stack backtraces under tail call
// optimization.
exit(0);
}
......@@ -635,6 +635,7 @@ main() {
readonly KS_BRAND_KEY="KSBrandID"
readonly QUARANTINE_ATTR="com.apple.quarantine"
readonly KEYCHAIN_REAUTHORIZE_DIR=".keychain_reauthorize"
# 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
......@@ -1492,6 +1493,51 @@ main() {
2> /dev/null
fi
# Do Keychain reauthorization. This involves running a stub executable on
# the dmg that loads the newly-updated framework and jumps to it to perform
# the reauthorization. The stub executable can be signed by the old
# certificate even after the rest of Chrome switches to the new certificate,
# so it still has access to the old Keychain items. The stub executable is
# an unbundled flat file executable whose name matches the real
# application's bundle identifier, so it's permitted access to the Keychain
# items. Doing a reauthorization step at update time reauthorizes Keychain
# items for users who never bother restarting Chrome, and provides a
# mechanism to continue doing reauthorizations even after the certificate
# changes. However, it only works for non-system ticket installations of
# Chrome, because the updater runs as root when on a system ticket, and root
# can't access individual user Keychains.
#
# Even if the reauthorization tool is launched, it doesn't necessarily try
# to do anything. It will only attempt to perform a reauthorization if one
# hasn't yet been done at update time.
note "maybe reauthorizing Keychain"
if [[ -z "${system_ticket}" ]]; then
local new_bundleid_app
new_bundleid_app="$(defaults read "${installed_app_plist}" \
"${APP_BUNDLEID_KEY}" || true)"
note "new_bundleid_app = ${new_bundleid_app}"
local keychain_reauthorize_dir="\
${update_dmg_mount_point}/${KEYCHAIN_REAUTHORIZE_DIR}"
local keychain_reauthorize_path="\
${keychain_reauthorize_dir}/${new_bundleid_app}"
note "keychain_reauthorize_path = ${keychain_reauthorize_path}"
if [[ -x "${keychain_reauthorize_path}" ]]; then
local framework_dir="${new_versioned_dir}/${FRAMEWORK_DIR}"
local framework_dylib_path="${framework_dir}/${FRAMEWORK_NAME}"
note "framework_dylib_path = ${framework_dylib_path}"
if [[ -f "${framework_dylib_path}" ]]; then
note "reauthorizing Keychain"
"${keychain_reauthorize_path}" "${framework_dylib_path}"
fi
fi
else
note "system ticket, not reauthorizing Keychain"
fi
# Great success!
note "done!"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment