#include <security_keychain/TrustStore.h>
#include <security_keychain/Globals.h>
#include <security_keychain/Certificate.h>
#include <security_keychain/KCCursor.h>
#include <security_keychain/SecCFTypes.h>
#include <security_cdsa_utilities/Schema.h>
namespace Security {
namespace KeychainCore {
TrustStore::TrustStore(Allocator &alloc)
: allocator(alloc), mRootsValid(false), mRootBytes(allocator)
{
}
TrustStore::~TrustStore()
{ }
SecTrustUserSetting TrustStore::find(Certificate *cert, Policy *policy)
{
if (Item item = findItem(cert, policy)) {
if (cert->keychain() == NULL) {
StorageManager::KeychainList keychains;
globals().storageManager.optionalSearchList((CFTypeRef)nil, keychains);
if (cert->findInKeychain(keychains) == NULL) {
Keychain defaultKeychain = Keychain::optional(NULL);
if (Keychain location = item->keychain()) {
try {
cert->copyTo(location); } catch (...) {
secdebug("trusteval", "failed to add certificate %p to keychain \"%s\"",
cert, location->name());
try {
if (&*location != &*defaultKeychain)
cert->copyTo(defaultKeychain); } catch (...) {
secdebug("trusteval", "failed to add certificate %p to keychain \"%s\"",
cert, defaultKeychain->name());
}
}
}
}
}
CssmDataContainer data;
item->getData(data);
if (data.length() != sizeof(TrustData))
MacOSError::throwMe(errSecInvalidTrustSetting);
TrustData &trust = *data.interpretedAs<TrustData>();
if (trust.version != UserTrustItem::currentVersion)
MacOSError::throwMe(errSecInvalidTrustSetting);
return trust.trust;
} else {
return kSecTrustResultUnspecified;
}
}
void TrustStore::assign(Certificate *cert, Policy *policy, SecTrustUserSetting trust)
{
TrustData trustData = { UserTrustItem::currentVersion, trust };
Keychain defaultKeychain = Keychain::optional(NULL);
Keychain trustLocation = defaultKeychain; if (Item item = findItem(cert, policy)) {
trustLocation = item->keychain();
if (trust == kSecTrustResultUnspecified)
item->keychain()->deleteItem(item);
else
item->modifyContent(NULL, sizeof(trustData), &trustData);
} else {
if (trust != kSecTrustResultUnspecified) {
Item item = new UserTrustItem(cert, policy, trustData);
if (Keychain location = cert->keychain()) {
try {
location->add(item); trustLocation = location;
} catch (...) {
if (&*location != &*defaultKeychain)
defaultKeychain->add(item); }
} else {
defaultKeychain->add(item); }
}
}
if (cert->keychain() == NULL) {
StorageManager::KeychainList keychains;
globals().storageManager.optionalSearchList((CFTypeRef)nil, keychains);
if (cert->findInKeychain(keychains) == NULL) {
try {
cert->copyTo(trustLocation); } catch (...) {
secdebug("trusteval", "failed to add certificate %p to keychain \"%s\"",
cert, trustLocation->name());
try {
if (&*trustLocation != &*defaultKeychain)
cert->copyTo(defaultKeychain); } catch (...) {
secdebug("trusteval", "failed to add certificate %p to keychain \"%s\"",
cert, defaultKeychain->name());
}
}
}
}
}
Item TrustStore::findItem(Certificate *cert, Policy *policy)
{
try {
SecKeychainAttribute attrs[2];
CssmAutoData certIndex(Allocator::standard());
UserTrustItem::makeCertIndex(cert, certIndex);
attrs[0].tag = kSecTrustCertAttr;
attrs[0].length = certIndex.length();
attrs[0].data = certIndex.data();
const CssmOid &policyOid = policy->oid();
attrs[1].tag = kSecTrustPolicyAttr;
attrs[1].length = policyOid.length();
attrs[1].data = policyOid.data();
SecKeychainAttributeList attrList = { 2, attrs };
KCCursor cursor = globals().storageManager.createCursor(CSSM_DL_DB_RECORD_USER_TRUST, &attrList);
Item item;
if (cursor->next(item))
return item;
else
return NULL;
} catch (const CommonError &error) {
return NULL; }
}
CFArrayRef TrustStore::copyRootCertificates()
{
if (!mRootsValid) {
loadRootCertificates();
mCFRoots = NULL;
}
if (!mCFRoots) {
uint32 count = mRoots.size();
secdebug("anchors", "building %ld CF-style anchor certificates", count);
vector<SecCertificateRef> roots(count);
for (uint32 n = 0; n < count; n++) {
SecPointer<Certificate> cert = new Certificate(mRoots[n],
CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER);
roots[n] = cert->handle();
}
mCFRoots = CFArrayCreate(NULL, (const void **)&roots[0], count,
&kCFTypeArrayCallBacks);
for (uint32 n = 0; n < count; n++)
CFRelease(roots[n]); }
CFRetain(mCFRoots);
return mCFRoots;
}
void TrustStore::getCssmRootCertificates(CertGroup &rootCerts)
{
if (!mRootsValid)
loadRootCertificates();
rootCerts = CertGroup(CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER, CSSM_CERTGROUP_DATA);
rootCerts.blobCerts() = &mRoots[0];
rootCerts.count() = mRoots.size();
}
void TrustStore::refreshRootCertificates()
{
if (mRootsValid) {
secdebug("anchors", "clearing %ld cached anchor certificates", mRoots.size());
if (mCFRoots) {
CFRelease(mCFRoots);
mCFRoots = NULL;
}
mRootBytes.reset();
mRoots.clear();
mRootsValid = false;
}
}
void TrustStore::loadRootCertificates()
{
using namespace CssmClient;
using namespace KeychainCore::Schema;
refreshRootCertificates();
static const char anchorLibrary[] = "/System/Library/Keychains/X509Anchors";
secdebug("anchors", "Loading anchors from %s", anchorLibrary);
DL dl(gGuidAppleFileDL);
Db db(dl, anchorLibrary);
DbCursor search(db);
search->recordType(CSSM_DL_DB_RECORD_X509_CERTIFICATE);
search->conjunctive(CSSM_DB_OR);
#if 0 // if we ever need to support v1/v2 certificates...
search->add(CSSM_DB_EQUAL, kX509CertificateCertType, UInt32(CSSM_CERT_X_509v1));
search->add(CSSM_DB_EQUAL, kX509CertificateCertType, UInt32(CSSM_CERT_X_509v2));
search->add(CSSM_DB_EQUAL, kX509CertificateCertType, UInt32(CSSM_CERT_X_509v3));
#endif
typedef list<CssmDataContainer> ContainerList;
ContainerList::iterator last;
ContainerList certs;
for (;;) {
DbUniqueRecord id;
last = certs.insert(certs.end(), CssmDataContainer());
if (!search->next(NULL, &*last, id))
break;
}
size_t size = 0;
for (ContainerList::const_iterator it = certs.begin(); it != last; it++)
size += it->length();
mRootBytes.length(size);
mRoots.clear();
uint8 *base = mRootBytes.data<uint8>();
for (ContainerList::const_iterator it = certs.begin(); it != last; it++) {
memcpy(base, it->data(), it->length());
mRoots.push_back(CssmData(base, it->length()));
base += it->length();
}
secdebug("anchors", "%ld anchors loaded", mRoots.size());
mRootsValid = true; }
} }