X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_keychain/lib/Policies.cpp?ds=sidebyside diff --git a/Security/libsecurity_keychain/lib/Policies.cpp b/Security/libsecurity_keychain/lib/Policies.cpp new file mode 100644 index 00000000..769a78bf --- /dev/null +++ b/Security/libsecurity_keychain/lib/Policies.cpp @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2002-2004,2011-2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// +// Policy.cpp - Working with Policies +// +#include +#include +#include +#include + +/* Oids longer than this are considered invalid. */ +#define MAX_OID_SIZE 32 + +//%%FIXME: need to use a common copy of this utility function +static +CFStringRef SecDERItemCopyOIDDecimalRepresentation(uint8 *oid, size_t oidLen) +{ + if (oidLen == 0) + return CFSTR(""); + + if (oidLen > MAX_OID_SIZE) + return CFSTR("Oid too long"); + + CFMutableStringRef result = CFStringCreateMutable(kCFAllocatorDefault, 0); + + // The first two levels are encoded into one byte, since the root level + // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then + // y may be > 39, so we have to add special-case handling for this. + uint32_t x = oid[0] / 40; + uint32_t y = oid[0] % 40; + if (x > 2) + { + // Handle special case for large y if x = 2 + y += (x - 2) * 40; + x = 2; + } + CFStringAppendFormat(result, NULL, CFSTR("%u.%u"), x, y); + + unsigned long value = 0; + for (x = 1; x < oidLen; ++x) + { + value = (value << 7) | (oid[x] & 0x7F); + /* @@@ value may not span more than 4 bytes. */ + /* A max number of 20 values is allowed. */ + 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()) +{ + // value is as yet unimplemented + secdebug("policy", "Policy() this %p", this); +} + +Policy::~Policy() throw() +{ + secdebug("policy", "~Policy() this %p", this); +} + +void Policy::setValue(const CssmData &value) +{ + StLock_(mMutex); + mValue = value; + mAuxValue.reset(); + + // Certain policy values may contain an embedded pointer. Ask me how I feel about that. + 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) + { + // Copy auxiliary data, then update the embedded pointer to reference our copy + mAuxValue.copy(const_cast(opts->ServerName), opts->ServerNameLen); + mValue.get().interpretedAs()->ServerName = + reinterpret_cast(mAuxValue.data()); + } + else + { + // Clear the embedded pointer! + mValue.get().interpretedAs()->ServerName = + reinterpret_cast(NULL); + } + } + } + else if (mOid == CSSMOID_APPLE_TP_SMIME || + mOid == CSSMOID_APPLE_TP_ICHAT || + mOid == CSSMOID_APPLE_TP_PASSBOOK_SIGNING) + { + 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) + { + // Copy auxiliary data, then update the embedded pointer to reference our copy + mAuxValue.copy(const_cast(opts->SenderEmail), opts->SenderEmailLen); + mValue.get().interpretedAs()->SenderEmail = + reinterpret_cast(mAuxValue.data()); + } + else + { + // Clear the embedded pointer! + mValue.get().interpretedAs()->SenderEmail = + reinterpret_cast(NULL); + } + } + } +} + +void Policy::setProperties(CFDictionaryRef properties) +{ + // Set the policy value based on the provided dictionary keys. + if (properties == NULL) + return; + + 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 = (unsigned)(strlen(buf)+1); // include terminating null + } + } + } + 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 || + mOid == CSSMOID_APPLE_TP_PASSBOOK_SIGNING) + { + 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)) { + CFStringRef teamIDStr = NULL; + if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyTeamIdentifier, (const void **)&teamIDStr)) { + char *buf2 = (char *)malloc(MAXPATHLEN); + if (buf2) { + if (CFStringGetCString(teamIDStr, buf2, MAXPATHLEN, kCFStringEncodingUTF8)) { + /* append tab separator and team identifier */ + strlcat(buf, "\t", MAXPATHLEN); + strlcat(buf, buf2, MAXPATHLEN); + } + free(buf2); + } + } + options.SenderEmail = buf; + options.SenderEmailLen = (unsigned)(strlen(buf)+1); // include terminating null + } + } + } + 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() +{ + // Builds and returns a dictionary which the caller must release. + CFMutableDictionaryRef properties = CFDictionaryCreateMutable(NULL, 0, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!properties) return NULL; + + // kSecPolicyOid + CFStringRef oidStr = SecDERItemCopyOIDDecimalRepresentation((uint8*)mOid.data(), mOid.length()); + if (oidStr) { + CFDictionarySetValue(properties, (const void *)kSecPolicyOid, (const void *)oidStr); + CFRelease(oidStr); + } + + // kSecPolicyName + if (mAuxValue) { + CFStringRef nameStr = CFStringCreateWithBytes(NULL, + (const UInt8 *)reinterpret_cast(mAuxValue.data()), + (CFIndex)mAuxValue.length(), kCFStringEncodingUTF8, false); + if (nameStr) { + if (mOid == CSSMOID_APPLE_TP_PASSBOOK_SIGNING) { + CFArrayRef strs = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, nameStr, CFSTR("\t")); + if (strs) { + CFIndex count = CFArrayGetCount(strs); + if (count > 0) + CFDictionarySetValue(properties, (const void *)kSecPolicyName, (const void *)CFArrayGetValueAtIndex(strs, 0)); + if (count > 1) + CFDictionarySetValue(properties, (const void *)kSecPolicyTeamIdentifier, (const void *)CFArrayGetValueAtIndex(strs, 1)); + CFRelease(strs); + } + } + else { + CFDictionarySetValue(properties, (const void *)kSecPolicyName, (const void *)nameStr); + } + CFRelease(nameStr); + } + } + + // kSecPolicyClient + 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); + } + } + } + + // key usage flags (currently only for S/MIME and iChat policies) + 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 +{ + //@@@ inefficient + return (oid() < other.oid()) || + (oid() == other.oid() && value() < other.value()); +} + +bool Policy::operator == (const Policy& other) const +{ + return oid() == other.oid() && value() == other.value(); +}