/* * 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(); }