1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/apps/app_shim/code_signature_mac.h"
#include "base/apple/bundle_locations.h"
#include "base/apple/foundation_util.h"
#include "base/apple/osstatus_logging.h"
#include "base/apple/scoped_cftyperef.h"
#include "base/debug/dump_without_crashing.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "components/crash/core/common/crash_key.h"
namespace {
// A crash key that is used when dumping because of errors when validating code
// signatures.
crash_reporter::CrashKeyString<256> code_signature_crash_key("CodeSignature");
// This function logs the status and error_details using OSSTATUS_LOG(). It also
// calls base::debug::DumpWithoutCrashing() using code_signature_crash_key
// as a crash key. The status and error_details are appended to the crash key.
void DumpOSStatusError(OSStatus status, std::string error_details) {
OSSTATUS_LOG(ERROR, status) << error_details;
crash_reporter::ScopedCrashKeyString crash_key_value(
&code_signature_crash_key,
base::StringPrintf("%s: %s (%d)", error_details.c_str(),
logging::DescriptionFromOSStatus(status).c_str(),
status));
base::debug::DumpWithoutCrashing();
}
// This function is similar to DumpOSStatusError(), however it operates without
// an OSStatus.
void DumpError(std::string error_details) {
LOG(ERROR) << error_details;
crash_reporter::ScopedCrashKeyString crash_key_value(
&code_signature_crash_key, error_details);
base::debug::DumpWithoutCrashing();
}
} // namespace
namespace apps {
base::expected<base::apple::ScopedCFTypeRef<CFStringRef>,
MissingRequirementReason>
FrameworkBundleDesignatedRequirementString() {
// Note: Don't validate |framework_code|: We don't need to waste time
// validating. We are only interested in discovering if the framework bundle
// is code-signed, and if so what the designated requirement is.
base::apple::ScopedCFTypeRef<CFURLRef> framework_url =
base::apple::FilePathToCFURL(base::apple::FrameworkBundlePath());
base::apple::ScopedCFTypeRef<SecStaticCodeRef> framework_code;
OSStatus status = SecStaticCodeCreateWithPath(
framework_url.get(), kSecCSDefaultFlags, framework_code.InitializeInto());
// If the framework bundle is unsigned there is nothing else to do. We treat
// this as success because there’s no identity to protect or even match, so
// it’s not dangerous to let the shim connect.
if (status == errSecCSUnsigned) {
return base::unexpected(MissingRequirementReason::NoOrAdHocSignature);
}
// If there was an error obtaining the SecStaticCodeRef something is very
// broken or something bad is happening, deny.
if (status != errSecSuccess) {
DumpOSStatusError(status, "SecStaticCodeCreateWithPath");
return base::unexpected(MissingRequirementReason::Error);
}
// Copy the signing info from the SecStaticCodeRef.
base::apple::ScopedCFTypeRef<CFDictionaryRef> framework_signing_info;
status = SecCodeCopySigningInformation(
framework_code.get(), kSecCSSigningInformation,
framework_signing_info.InitializeInto());
if (status != errSecSuccess) {
DumpOSStatusError(status, "SecCodeCopySigningInformation");
return base::unexpected(MissingRequirementReason::Error);
}
// Look up the code signing flags. If the flags are absent treat this as
// unsigned. This decision is consistent with the StaticCode source:
// https://github.com/apple-oss-distributions/Security/blob/Security-60157.40.30.0.1/OSX/libsecurity_codesigning/lib/StaticCode.cpp#L2270
CFNumberRef framework_signing_info_flags =
base::apple::GetValueFromDictionary<CFNumberRef>(
framework_signing_info.get(), kSecCodeInfoFlags);
if (!framework_signing_info_flags) {
return base::unexpected(MissingRequirementReason::NoOrAdHocSignature);
}
// If the framework bundle is ad-hoc signed there is nothing else to
// do. While the framework bundle is code-signed an ad-hoc signature does not
// contain any identities to match against. Treat this as a success.
//
// Note: Using a long long to extract the value from the CFNumberRef to be
// consistent with how it was packed by Security.framework.
// https://github.com/apple-oss-distributions/Security/blob/Security-60157.40.30.0.1/OSX/libsecurity_utilities/lib/cfutilities.h#L262
long long flags;
if (!CFNumberGetValue(framework_signing_info_flags, kCFNumberLongLongType,
&flags)) {
DumpError("CFNumberGetValue");
return base::unexpected(MissingRequirementReason::Error);
}
if (static_cast<uint32_t>(flags) & kSecCodeSignatureAdhoc) {
return base::unexpected(MissingRequirementReason::NoOrAdHocSignature);
}
// Time to start building a requirement that we will use to validate
// another code signature. First let's get the framework bundle requirement.
// We will build a suitable requirement based off that.
base::apple::ScopedCFTypeRef<SecRequirementRef> framework_requirement;
status =
SecCodeCopyDesignatedRequirement(framework_code.get(), kSecCSDefaultFlags,
framework_requirement.InitializeInto());
if (status != errSecSuccess) {
DumpOSStatusError(status, "SecCodeCopyDesignatedRequirement");
return base::unexpected(MissingRequirementReason::Error);
}
base::apple::ScopedCFTypeRef<CFStringRef> framework_requirement_string;
status =
SecRequirementCopyString(framework_requirement.get(), kSecCSDefaultFlags,
framework_requirement_string.InitializeInto());
if (status != errSecSuccess) {
DumpOSStatusError(status, "SecRequirementCopyString");
DumpOSStatusError(status, "SecCodeCopyDesignatedRequirement");
}
return framework_requirement_string;
}
base::apple::ScopedCFTypeRef<SecRequirementRef> RequirementFromString(
CFStringRef requirement_string) {
base::apple::ScopedCFTypeRef<SecRequirementRef> requirement;
OSStatus status = SecRequirementCreateWithString(
requirement_string, kSecCSDefaultFlags, requirement.InitializeInto());
if (status != errSecSuccess) {
DumpOSStatusError(status,
std::string("SecRequirementCreateWithString: ") +
base::SysCFStringRefToUTF8(requirement_string));
return base::apple::ScopedCFTypeRef<SecRequirementRef>(nullptr);
}
return requirement;
}
} // namespace apps
|