chromium/chrome/browser/extensions/api/enterprise_reporting_private/keychain_data_helper_mac.cc

// Copyright 2020 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/extensions/api/enterprise_reporting_private/keychain_data_helper_mac.h"

#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>

#include "base/apple/foundation_util.h"
#include "base/apple/scoped_cftyperef.h"
#include "base/strings/sys_string_conversions.h"

namespace extensions {
namespace {

// Much of the Keychain API was marked deprecated as of the macOS 13 SDK.
// Removal of its use is tracked in https://crbug.com/1348251 but deprecation
// warnings are disabled in the meanwhile.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"

// Creates an access for a generic password item to share it with other Google
// applications with teamid:EQHXZ8M8AV (taken from the signing certificate).
OSStatus CreateTargetAccess(CFStringRef service_name,
                            SecAccessRef* access_ref) {
  OSStatus status = noErr;
  status = SecAccessCreate(service_name, /*trustedlist=*/nullptr, access_ref);
  if (status != noErr) {
    return status;
  }

  base::apple::ScopedCFTypeRef<CFArrayRef> acl_list;
  status = SecAccessCopyACLList(*access_ref, acl_list.InitializeInto());
  if (status != noErr) {
    return status;
  }

  for (CFIndex i = 0; i < CFArrayGetCount(acl_list.get()); ++i) {
    SecACLRef acl = (SecACLRef)CFArrayGetValueAtIndex(acl_list.get(), i);

    base::apple::ScopedCFTypeRef<CFArrayRef> app_list;
    base::apple::ScopedCFTypeRef<CFStringRef> description;
    SecKeychainPromptSelector dummy_prompt_selector;
    status = SecACLCopyContents(acl, app_list.InitializeInto(),
                                description.InitializeInto(),
                                &dummy_prompt_selector);
    if (status != noErr) {
      return status;
    }

    // Replace explicit non-empty app list with void to allow any application
    if (app_list && CFArrayGetCount(app_list.get())) {
      status = SecACLSetContents(acl, /*applicationList=*/nullptr,
                                 description.get(), dummy_prompt_selector);
      if (status != noErr) {
        return status;
      }
    }
  }

  return noErr;
}

// Verifies whether `keychain` is currently locked. Returns the given OSStatus
// and, if the status is successful, sets `unlocked` to the value representing
// whether `keychain` is currently unlocked or not.
OSStatus VerifyKeychainUnlocked(SecKeychainRef keychain, bool* unlocked) {
  SecKeychainStatus keychain_status;
  OSStatus status = SecKeychainGetStatus(keychain, &keychain_status);
  if (status != noErr) {
    return status;
  }

  *unlocked = keychain_status & kSecUnlockStateStatus;
  return status;
}

}  // namespace

OSStatus WriteKeychainItem(const std::string& service_name,
                           const std::string& account_name,
                           const std::string& password) {
  SecKeychainAttribute attributes[] = {
      {kSecLabelItemAttr, static_cast<UInt32>(service_name.length()),
       const_cast<char*>(service_name.data())},
      {kSecDescriptionItemAttr, 0U, nullptr},
      {kSecGenericItemAttr, 0U, nullptr},
      {kSecCommentItemAttr, 0U, nullptr},
      {kSecServiceItemAttr, static_cast<UInt32>(service_name.length()),
       const_cast<char*>(service_name.data())},
      {kSecAccountItemAttr, static_cast<UInt32>(account_name.length()),
       const_cast<char*>(account_name.data())}};
  SecKeychainAttributeList attribute_list = {std::size(attributes), attributes};

  base::apple::ScopedCFTypeRef<SecAccessRef> access_ref;
  OSStatus status =
      CreateTargetAccess(base::SysUTF8ToCFStringRef(service_name).get(),
                         access_ref.InitializeInto());
  if (status != noErr) {
    return status;
  }
  return SecKeychainItemCreateFromContent(
      kSecGenericPasswordItemClass, &attribute_list,
      static_cast<UInt32>(password.size()), password.data(), nullptr,
      access_ref.get(), nullptr);
}

OSStatus VerifyKeychainForItemUnlocked(SecKeychainItemRef item_ref,
                                       bool* unlocked) {
  base::apple::ScopedCFTypeRef<SecKeychainRef> keychain;
  OSStatus status =
      SecKeychainItemCopyKeychain(item_ref, keychain.InitializeInto());
  if (status != noErr) {
    return status;
  }

  return VerifyKeychainUnlocked(keychain.get(), unlocked);
}

OSStatus VerifyDefaultKeychainUnlocked(bool* unlocked) {
  base::apple::ScopedCFTypeRef<SecKeychainRef> keychain;
  OSStatus status = SecKeychainCopyDefault(keychain.InitializeInto());
  if (status != noErr) {
    return status;
  }

  return VerifyKeychainUnlocked(keychain.get(), unlocked);
}

#pragma clang diagnostic pop

}  // namespace extensions