#include <security_keychain/Policies.h>
#include <security_utilities/debugging.h>
#include <Security/oidsalg.h>
#include <sys/param.h>
#define MAX_OID_SIZE 32
CFStringRef SecDERItemCopyOIDDecimalRepresentation(uint8 *oid, size_t oidLen)
{
if (oidLen == 0)
return CFSTR("<NULL>");
if (oidLen > MAX_OID_SIZE)
return CFSTR("Oid too long");
CFMutableStringRef result = CFStringCreateMutable(kCFAllocatorDefault, 0);
uint32_t x = oid[0] / 40;
uint32_t y = oid[0] % 40;
if (x > 2)
{
y += (x - 2) * 40;
x = 2;
}
CFStringAppendFormat(result, NULL, CFSTR("%u.%u"), x, y);
uint32_t value = 0;
for (x = 1; x < oidLen; ++x)
{
value = (value << 7) | (oid[x] & 0x7F);
if (!(oid[x] & 0x80))
{
CFStringAppendFormat(result, NULL, CFSTR(".%lu"), value);
value = 0;
}
}
return result;
}
using namespace KeychainCore;
Policy::Policy(TP supportingTp, const CssmOid &policyOid)
: mTp(supportingTp),
mOid(Allocator::standard(), policyOid),
mValue(Allocator::standard()),
mAuxValue(Allocator::standard())
{
secdebug("policy", "Policy() this %p", this);
}
Policy::~Policy() throw()
{
secdebug("policy", "~Policy() this %p", this);
}
void Policy::setValue(const CssmData &value)
{
StLock<Mutex>_(mMutex);
mValue = value;
mAuxValue.reset();
if (mOid == CSSMOID_APPLE_TP_SSL ||
mOid == CSSMOID_APPLE_TP_EAP ||
mOid == CSSMOID_APPLE_TP_IP_SEC ||
mOid == CSSMOID_APPLE_TP_APPLEID_SHARING)
{
CSSM_APPLE_TP_SSL_OPTIONS *opts = (CSSM_APPLE_TP_SSL_OPTIONS *)value.data();
if (opts->Version == CSSM_APPLE_TP_SSL_OPTS_VERSION)
{
if (opts->ServerNameLen > 0)
{
mAuxValue.copy(const_cast<char*>(opts->ServerName), opts->ServerNameLen);
mValue.get().interpretedAs<CSSM_APPLE_TP_SSL_OPTIONS>()->ServerName =
reinterpret_cast<char*>(mAuxValue.data());
}
else
{
mValue.get().interpretedAs<CSSM_APPLE_TP_SSL_OPTIONS>()->ServerName =
reinterpret_cast<char*>(NULL);
}
}
}
else if (mOid == CSSMOID_APPLE_TP_SMIME ||
mOid == CSSMOID_APPLE_TP_ICHAT)
{
CSSM_APPLE_TP_SMIME_OPTIONS *opts = (CSSM_APPLE_TP_SMIME_OPTIONS *)value.data();
if (opts->Version == CSSM_APPLE_TP_SMIME_OPTS_VERSION)
{
if (opts->SenderEmailLen > 0)
{
mAuxValue.copy(const_cast<char*>(opts->SenderEmail), opts->SenderEmailLen);
mValue.get().interpretedAs<CSSM_APPLE_TP_SMIME_OPTIONS>()->SenderEmail =
reinterpret_cast<char*>(mAuxValue.data());
}
else
{
mValue.get().interpretedAs<CSSM_APPLE_TP_SMIME_OPTIONS>()->SenderEmail =
reinterpret_cast<char*>(NULL);
}
}
}
}
void Policy::setProperties(CFDictionaryRef properties)
{
if (mOid == CSSMOID_APPLE_TP_SSL ||
mOid == CSSMOID_APPLE_TP_EAP ||
mOid == CSSMOID_APPLE_TP_IP_SEC ||
mOid == CSSMOID_APPLE_TP_APPLEID_SHARING)
{
CSSM_APPLE_TP_SSL_OPTIONS options = { CSSM_APPLE_TP_SSL_OPTS_VERSION, 0, NULL, 0 };
char *buf = NULL;
CFStringRef nameStr = NULL;
if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyName, (const void **)&nameStr)) {
buf = (char *)malloc(MAXPATHLEN);
if (buf) {
if (CFStringGetCString(nameStr, buf, MAXPATHLEN, kCFStringEncodingUTF8)) {
options.ServerName = buf;
options.ServerNameLen = strlen(buf)+1; }
}
}
CFBooleanRef clientRef = NULL;
if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyClient, (const void **)&clientRef)
&& CFBooleanGetValue(clientRef) == true)
options.Flags |= CSSM_APPLE_TP_SSL_CLIENT;
const CssmData value((uint8*)&options, sizeof(options));
this->setValue(value);
if (buf) free(buf);
}
else if (mOid == CSSMOID_APPLE_TP_SMIME ||
mOid == CSSMOID_APPLE_TP_ICHAT)
{
CSSM_APPLE_TP_SMIME_OPTIONS options = { CSSM_APPLE_TP_SMIME_OPTS_VERSION, 0, 0, NULL };
char *buf = NULL;
CFStringRef nameStr = NULL;
if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyName, (const void **)&nameStr)) {
buf = (char *)malloc(MAXPATHLEN);
if (buf) {
if (CFStringGetCString(nameStr, buf, MAXPATHLEN, kCFStringEncodingUTF8)) {
options.SenderEmail = buf;
options.SenderEmailLen = strlen(buf)+1; }
}
}
CFBooleanRef kuRef = NULL;
if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_DigitalSignature, (const void **)&kuRef)
&& CFBooleanGetValue(kuRef) == true)
options.IntendedUsage |= CE_KU_DigitalSignature;
if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_NonRepudiation, (const void **)&kuRef)
&& CFBooleanGetValue(kuRef) == true)
options.IntendedUsage |= CE_KU_NonRepudiation;
if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_KeyEncipherment, (const void **)&kuRef)
&& CFBooleanGetValue(kuRef) == true)
options.IntendedUsage |= CE_KU_KeyEncipherment;
if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_DataEncipherment, (const void **)&kuRef)
&& CFBooleanGetValue(kuRef) == true)
options.IntendedUsage |= CE_KU_DataEncipherment;
if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_KeyAgreement, (const void **)&kuRef)
&& CFBooleanGetValue(kuRef) == true)
options.IntendedUsage |= CE_KU_KeyAgreement;
if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_KeyCertSign, (const void **)&kuRef)
&& CFBooleanGetValue(kuRef) == true)
options.IntendedUsage |= CE_KU_KeyCertSign;
if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_CRLSign, (const void **)&kuRef)
&& CFBooleanGetValue(kuRef) == true)
options.IntendedUsage |= CE_KU_CRLSign;
if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_EncipherOnly, (const void **)&kuRef)
&& CFBooleanGetValue(kuRef) == true)
options.IntendedUsage |= CE_KU_EncipherOnly;
if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_DecipherOnly, (const void **)&kuRef)
&& CFBooleanGetValue(kuRef) == true)
options.IntendedUsage |= CE_KU_DecipherOnly;
const CssmData value((uint8*)&options, sizeof(options));
this->setValue(value);
if (buf) free(buf);
}
}
CFDictionaryRef Policy::properties()
{
CSSM_DATA value = this->value();
CFMutableDictionaryRef properties = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!properties) return NULL;
CFStringRef oidStr = SecDERItemCopyOIDDecimalRepresentation((uint8*)mOid.data(), mOid.length());
if (oidStr) {
CFDictionarySetValue(properties, (const void *)kSecPolicyOid, (const void *)oidStr);
CFRelease(oidStr);
}
if (mAuxValue) {
CFStringRef nameStr = CFStringCreateWithBytes(NULL,
(const UInt8 *)reinterpret_cast<char*>(mAuxValue.data()),
(CFIndex)mAuxValue.length(), kCFStringEncodingUTF8, false);
if (nameStr) {
CFDictionarySetValue(properties, (const void *)kSecPolicyName, (const void *)nameStr);
CFRelease(nameStr);
}
}
if (mValue) {
if (mOid == CSSMOID_APPLE_TP_SSL ||
mOid == CSSMOID_APPLE_TP_EAP ||
mOid == CSSMOID_APPLE_TP_IP_SEC ||
mOid == CSSMOID_APPLE_TP_APPLEID_SHARING)
{
CSSM_APPLE_TP_SSL_OPTIONS *opts = (CSSM_APPLE_TP_SSL_OPTIONS *)mValue.data();
if (opts->Flags & CSSM_APPLE_TP_SSL_CLIENT) {
CFDictionarySetValue(properties, (const void *)kSecPolicyClient, (const void *)kCFBooleanTrue);
}
}
}
if (mValue) {
if (mOid == CSSMOID_APPLE_TP_SMIME ||
mOid == CSSMOID_APPLE_TP_ICHAT)
{
CSSM_APPLE_TP_SMIME_OPTIONS *opts = (CSSM_APPLE_TP_SMIME_OPTIONS *)mValue.data();
CE_KeyUsage usage = opts->IntendedUsage;
if (usage & CE_KU_DigitalSignature)
CFDictionarySetValue(properties, (const void *)kSecPolicyKU_DigitalSignature, (const void *)kCFBooleanTrue);
if (usage & CE_KU_NonRepudiation)
CFDictionarySetValue(properties, (const void *)kSecPolicyKU_NonRepudiation, (const void *)kCFBooleanTrue);
if (usage & CE_KU_KeyEncipherment)
CFDictionarySetValue(properties, (const void *)kSecPolicyKU_KeyEncipherment, (const void *)kCFBooleanTrue);
if (usage & CE_KU_DataEncipherment)
CFDictionarySetValue(properties, (const void *)kSecPolicyKU_DataEncipherment, (const void *)kCFBooleanTrue);
if (usage & CE_KU_KeyAgreement)
CFDictionarySetValue(properties, (const void *)kSecPolicyKU_KeyAgreement, (const void *)kCFBooleanTrue);
if (usage & CE_KU_KeyCertSign)
CFDictionarySetValue(properties, (const void *)kSecPolicyKU_KeyCertSign, (const void *)kCFBooleanTrue);
if (usage & CE_KU_CRLSign)
CFDictionarySetValue(properties, (const void *)kSecPolicyKU_CRLSign, (const void *)kCFBooleanTrue);
if (usage & CE_KU_EncipherOnly)
CFDictionarySetValue(properties, (const void *)kSecPolicyKU_EncipherOnly, (const void *)kCFBooleanTrue);
if (usage & CE_KU_DecipherOnly)
CFDictionarySetValue(properties, (const void *)kSecPolicyKU_DecipherOnly, (const void *)kCFBooleanTrue);
}
}
return properties;
}
bool Policy::operator < (const Policy& other) const
{
return oid() < other.oid() ||
oid() == other.oid() && value() < other.value();
}
bool Policy::operator == (const Policy& other) const
{
return oid() == other.oid() && value() == other.value();
}
bool Policy::equal(SecCFObject &other)
{
return (*this) == (const Policy &)other;
}