#include <Security/TrustStore.h>
#include <Security/Globals.h>
#include <Security/Certificate.h>
#include <Security/SecCFTypes.h>
#include <Security/schema.h>
namespace Security {
namespace KeychainCore {
TrustStore::TrustStore(CssmAllocator &alloc)
: allocator(alloc), mRootsValid(false), mRootBytes(allocator)
{
}
TrustStore::~TrustStore()
{ }
SecTrustUserSetting TrustStore::find(Certificate *cert, Policy *policy)
{
if (Item item = findItem(cert, policy)) {
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 };
if (Item item = findItem(cert, policy)) {
item->modifyContent(NULL, sizeof(trustData), &trustData);
} else {
Item item = new UserTrustItem(cert, policy, trustData);
if (Keychain location = cert->keychain())
location->add(item); else
Keychain::optional(NULL)->add(item); }
}
Item TrustStore::findItem(Certificate *cert, Policy *policy)
{
try {
SecKeychainAttribute attrs[2];
const CssmData &data = cert->data();
attrs[0].tag = kSecTrustCertAttr;
attrs[0].length = data.length();
attrs[0].data = data.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 CssmError &error) {
if (error.cssmError() == CSSMERR_DL_INVALID_RECORDTYPE)
return NULL; throw;
}
}
CFArrayRef TrustStore::copyRootCertificates()
{
if (!mRootsValid) {
loadRootCertificates();
mCFRoots = NULL;
}
if (!mCFRoots) {
uint32 count = mRoots.size();
debug("anchors", "building %ld CF-style anchor certificates", count);
vector<SecCertificateRef> roots(count);
for (uint32 n = 0; n < count; n++) {
RefPointer<Certificate> cert = new Certificate(mRoots[n],
CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER);
roots[n] = gTypes().certificate.handle(*cert);
}
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) {
debug("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";
debug("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());
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();
}
debug("anchors", "%ld anchors loaded", mRoots.size());
mRootsValid = true; }
} }