X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_keychain/lib/Policies.cpp diff --git a/libsecurity_keychain/lib/Policies.cpp b/libsecurity_keychain/lib/Policies.cpp new file mode 100644 index 00000000..2957cac9 --- /dev/null +++ b/libsecurity_keychain/lib/Policies.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2002-2004 Apple Computer, 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 +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); + + uint32_t 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) + { + 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 (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; // 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) + { + 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; // 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. + CSSM_DATA value = this->value(); + 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) { + 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(); +} + +bool Policy::equal(SecCFObject &other) +{ + return (*this) == (const Policy &)other; +}