blob: 1979d1bd0b015463ff1bc5b659e0c7e94e700afb [file] [log] [blame]
// Copyright 2012 The Goma Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "openssl_engine_helper.h"
#include <string>
#include <Security/SecImportExport.h>
#include <Security/SecKeychainSearch.h>
#include <Security/SecKey.h>
#include <Security/SecCertificate.h>
#include <CoreFoundation/CoreFoundation.h>
#include "glog/logging.h"
using std::string;
namespace {
// http://www.apple.com/certificateauthority/ca_program.html
const char* kRootKeychainStore = "/System/Library/Keychains/X509Anchors";
// Searches / retrieves certificates.
// TODO: replace several deprecated functions, which I could not
// find alternative functions to use.
OSStatus ReadCertsFromKeychain(SecKeychainRef chain_ref,
CFMutableArrayRef out_array,
int *num_items) {
OSStatus ret;
SecKeychainSearchRef search_ref;
ret = SecKeychainSearchCreateFromAttributes(chain_ref,
kSecCertificateItemClass,
nullptr,
&search_ref);
if (ret)
return ret;
for (;;) {
SecKeychainItemRef item_ref;
ret = SecKeychainSearchCopyNext(search_ref, &item_ref);
if (ret) {
if (ret == errSecItemNotFound)
ret = noErr;
break;
}
CFArrayAppendValue(out_array, item_ref);
CFRelease(item_ref);
(*num_items)++;
}
CFRelease(search_ref);
return ret;
}
// Converts certificates to string with PEM format.
OSStatus WriteCertsToMemory(CFMutableArrayRef export_items, string* certs) {
CFDataRef export_data;
OSStatus ret;
ret = SecKeychainItemExport(export_items, kSecFormatPEMSequence,
kSecItemPemArmour, nullptr, &export_data);
if (ret)
return ret;
certs->assign(reinterpret_cast<const char*>(CFDataGetBytePtr(export_data)),
CFDataGetLength(export_data));
CFRelease(export_data);
return ret;
}
// Dumps the system root certificates to |out_certs| with PEM format.
bool DumpCertificates(SecKeychainRef keychain_ref, string* out_certs) {
OSStatus ret;
int num_certs;
CFMutableArrayRef export_items = CFArrayCreateMutable(
nullptr, 0, &kCFTypeArrayCallBacks);
if (export_items == nullptr) {
LOG(ERROR) << "Failed to allocate memory for certificates.";
return false;
}
ret = ReadCertsFromKeychain(keychain_ref, export_items, &num_certs);
if (ret) {
LOG(ERROR) << "Failed to read root certificates keychain:"
<< " ret=" << ret;
CFRelease(export_items);
return false;
}
ret = WriteCertsToMemory(export_items, out_certs);
if (ret) {
LOG(ERROR) << "Failed to copy root certificates keychain:"
<< " ret=" << ret;
CFRelease(export_items);
return false;
}
CFRelease(export_items);
return true;
}
} // namespace
namespace devtools_goma {
bool GetTrustedRootCerts(string* certs) {
OSStatus err_status;
SecKeychainRef keychain_ref;
bool ret = false;
err_status = SecKeychainOpen(kRootKeychainStore, &keychain_ref);
if (err_status) {
LOG(ERROR) << "Failed to open root certificates keychain:"
<< kRootKeychainStore
<< " ret=" << err_status;
return false;
}
ret = DumpCertificates(keychain_ref, certs);
CFRelease(keychain_ref);
return ret;
}
} // namespace devtools_goma