/ OSX / libsecurity_keychain / lib / Policies.cpp
Policies.cpp
  1  /*
  2   * Copyright (c) 2002-2004,2011-2015 Apple Inc. All Rights Reserved.
  3   *
  4   * @APPLE_LICENSE_HEADER_START@
  5   *
  6   * This file contains Original Code and/or Modifications of Original Code
  7   * as defined in and that are subject to the Apple Public Source License
  8   * Version 2.0 (the 'License'). You may not use this file except in
  9   * compliance with the License. Please obtain a copy of the License at
 10   * http://www.opensource.apple.com/apsl/ and read it before using this
 11   * file.
 12   *
 13   * The Original Code and all software distributed under the License are
 14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 18   * Please see the License for the specific language governing rights and
 19   * limitations under the License.
 20   *
 21   * @APPLE_LICENSE_HEADER_END@
 22   */
 23  
 24  //
 25  // Policy.cpp - Working with Policies
 26  //
 27  #include <security_keychain/Policies.h>
 28  #include <security_utilities/debugging.h>
 29  #include <Security/oidsalg.h>
 30  #include <sys/param.h>
 31  
 32  /* Oids longer than this are considered invalid. */
 33  #define MAX_OID_SIZE				32
 34  
 35  //%%FIXME: need to use a common copy of this utility function
 36  static
 37  CFStringRef SecDERItemCopyOIDDecimalRepresentation(uint8 *oid, size_t oidLen)
 38  {
 39  	if (oidLen == 0)
 40  		return CFSTR("<NULL>");
 41  
 42  	if (oidLen > MAX_OID_SIZE)
 43  		return CFSTR("Oid too long");
 44  
 45  	CFMutableStringRef result = CFStringCreateMutable(kCFAllocatorDefault, 0);
 46  
 47  	// The first two levels are encoded into one byte, since the root level
 48  	// has only 3 nodes (40*x + y).  However if x = joint-iso-itu-t(2) then
 49  	// y may be > 39, so we have to add special-case handling for this.
 50  	uint32_t x = oid[0] / 40;
 51  	uint32_t y = oid[0] % 40;
 52  	if (x > 2)
 53  	{
 54  		// Handle special case for large y if x = 2
 55  		y += (x - 2) * 40;
 56  		x = 2;
 57  	}
 58  	CFStringAppendFormat(result, NULL, CFSTR("%u.%u"), x, y);
 59  
 60  	unsigned long value = 0;
 61  	for (x = 1; x < oidLen; ++x)
 62  	{
 63  		value = (value << 7) | (oid[x] & 0x7F);
 64  		/* @@@ value may not span more than 4 bytes. */
 65  		/* A max number of 20 values is allowed. */
 66  		if (!(oid[x] & 0x80))
 67  		{
 68  			CFStringAppendFormat(result, NULL, CFSTR(".%lu"), value);
 69  			value = 0;
 70  		}
 71  	}
 72  	return result;
 73  }
 74  
 75  
 76  using namespace KeychainCore;
 77  
 78  Policy::Policy(TP supportingTp, const CssmOid &policyOid)
 79      : mTp(supportingTp),
 80        mOid(Allocator::standard(), policyOid),
 81        mValue(Allocator::standard()),
 82        mAuxValue(Allocator::standard())
 83  {
 84  	// value is as yet unimplemented
 85  	secinfo("policy", "Policy() this %p", this);
 86  }
 87  
 88  Policy::~Policy() _NOEXCEPT
 89  {
 90  	secinfo("policy", "~Policy() this %p", this);
 91  }
 92  
 93  void Policy::setValue(const CssmData &value)
 94  {
 95  	StLock<Mutex>_(mMutex);
 96  	mValue = value;
 97  	mAuxValue.reset();
 98  
 99  	// Certain policy values may contain an embedded pointer. Ask me how I feel about that.
100  	if (mOid == CSSMOID_APPLE_TP_SSL ||
101  		mOid == CSSMOID_APPLE_TP_EAP ||
102  		mOid == CSSMOID_APPLE_TP_IP_SEC ||
103  		mOid == CSSMOID_APPLE_TP_APPLEID_SHARING)
104  	{
105  		CSSM_APPLE_TP_SSL_OPTIONS *opts = (CSSM_APPLE_TP_SSL_OPTIONS *)value.data();
106  		if (opts->Version == CSSM_APPLE_TP_SSL_OPTS_VERSION)
107  		{
108  			if (opts->ServerNameLen > 0)
109  			{
110  				// Copy auxiliary data, then update the embedded pointer to reference our copy
111  				mAuxValue.copy(const_cast<char*>(opts->ServerName), opts->ServerNameLen);
112  				mValue.get().interpretedAs<CSSM_APPLE_TP_SSL_OPTIONS>()->ServerName =
113  					reinterpret_cast<char*>(mAuxValue.data());
114  			}
115  			else
116  			{
117  				// Clear the embedded pointer!
118  				mValue.get().interpretedAs<CSSM_APPLE_TP_SSL_OPTIONS>()->ServerName =
119  					reinterpret_cast<char*>(NULL);
120  			}
121  		}
122  	}
123  	else if (mOid == CSSMOID_APPLE_TP_SMIME ||
124  			mOid == CSSMOID_APPLE_TP_ICHAT ||
125  			mOid == CSSMOID_APPLE_TP_PASSBOOK_SIGNING)
126  	{
127  		CSSM_APPLE_TP_SMIME_OPTIONS *opts = (CSSM_APPLE_TP_SMIME_OPTIONS *)value.data();
128  		if (opts->Version == CSSM_APPLE_TP_SMIME_OPTS_VERSION)
129  		{
130  			if (opts->SenderEmailLen > 0)
131  			{
132  				// Copy auxiliary data, then update the embedded pointer to reference our copy
133  				mAuxValue.copy(const_cast<char*>(opts->SenderEmail), opts->SenderEmailLen);
134  				mValue.get().interpretedAs<CSSM_APPLE_TP_SMIME_OPTIONS>()->SenderEmail =
135  					reinterpret_cast<char*>(mAuxValue.data());
136  			}
137  			else
138  			{
139  				// Clear the embedded pointer!
140  				mValue.get().interpretedAs<CSSM_APPLE_TP_SMIME_OPTIONS>()->SenderEmail =
141  					reinterpret_cast<char*>(NULL);
142  			}
143  		}
144  	}
145  }
146  
147  void Policy::setProperties(CFDictionaryRef properties)
148  {
149  	// Set the policy value based on the provided dictionary keys.
150  	if (properties == NULL)
151  		return;
152  
153  	if (mOid == CSSMOID_APPLE_TP_SSL ||
154  		mOid == CSSMOID_APPLE_TP_EAP ||
155  		mOid == CSSMOID_APPLE_TP_IP_SEC ||
156  		mOid == CSSMOID_APPLE_TP_APPLEID_SHARING)
157  	{
158  		CSSM_APPLE_TP_SSL_OPTIONS options = { CSSM_APPLE_TP_SSL_OPTS_VERSION, 0, NULL, 0 };
159  		char *buf = NULL;
160  		CFStringRef nameStr = NULL;
161  		if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyName, (const void **)&nameStr)) {
162  			buf = (char *)malloc(MAXPATHLEN);
163  			if (buf) {
164  				if (CFStringGetCString(nameStr, buf, MAXPATHLEN, kCFStringEncodingUTF8)) {
165  					options.ServerName = buf;
166  					options.ServerNameLen = (unsigned)(strlen(buf)+1); // include terminating null
167  				}
168  			}
169  		}
170  		CFBooleanRef clientRef = NULL;
171  		if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyClient, (const void **)&clientRef)
172  			&& CFBooleanGetValue(clientRef) == true)
173  			options.Flags |= CSSM_APPLE_TP_SSL_CLIENT;
174  
175  		const CssmData value((uint8*)&options, sizeof(options));
176  		this->setValue(value);
177  
178  		if (buf) free(buf);
179  	}
180  	else if (mOid == CSSMOID_APPLE_TP_SMIME ||
181  			mOid == CSSMOID_APPLE_TP_ICHAT ||
182  			mOid == CSSMOID_APPLE_TP_PASSBOOK_SIGNING)
183  	{
184  		CSSM_APPLE_TP_SMIME_OPTIONS options = { CSSM_APPLE_TP_SMIME_OPTS_VERSION, 0, 0, NULL };
185  		char *buf = NULL;
186  		CFStringRef nameStr = NULL;
187  		if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyName, (const void **)&nameStr)) {
188  			buf = (char *)malloc(MAXPATHLEN);
189  			if (buf) {
190  				if (CFStringGetCString(nameStr, buf, MAXPATHLEN, kCFStringEncodingUTF8)) {
191  					CFStringRef teamIDStr = NULL;
192  					if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyTeamIdentifier, (const void **)&teamIDStr)) {
193  						char *buf2 = (char *)malloc(MAXPATHLEN);
194  						if (buf2) {
195  							if (CFStringGetCString(teamIDStr, buf2, MAXPATHLEN, kCFStringEncodingUTF8)) {
196  								/* append tab separator and team identifier */
197  								strlcat(buf, "\t", MAXPATHLEN);
198  								strlcat(buf, buf2, MAXPATHLEN);
199  							}
200  							free(buf2);
201  						}
202  					}
203  					options.SenderEmail = buf;
204  					options.SenderEmailLen = (unsigned)(strlen(buf)+1); // include terminating null
205  				}
206  			}
207  		}
208  		CFBooleanRef kuRef = NULL;
209  		if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_DigitalSignature, (const void **)&kuRef)
210  			&& CFBooleanGetValue(kuRef) == true)
211  			options.IntendedUsage |= CE_KU_DigitalSignature;
212  		if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_NonRepudiation, (const void **)&kuRef)
213  			&& CFBooleanGetValue(kuRef) == true)
214  			options.IntendedUsage |= CE_KU_NonRepudiation;
215  		if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_KeyEncipherment, (const void **)&kuRef)
216  			&& CFBooleanGetValue(kuRef) == true)
217  			options.IntendedUsage |= CE_KU_KeyEncipherment;
218  		if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_DataEncipherment, (const void **)&kuRef)
219  			&& CFBooleanGetValue(kuRef) == true)
220  			options.IntendedUsage |= CE_KU_DataEncipherment;
221  		if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_KeyAgreement, (const void **)&kuRef)
222  			&& CFBooleanGetValue(kuRef) == true)
223  			options.IntendedUsage |= CE_KU_KeyAgreement;
224  		if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_KeyCertSign, (const void **)&kuRef)
225  			&& CFBooleanGetValue(kuRef) == true)
226  			options.IntendedUsage |= CE_KU_KeyCertSign;
227  		if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_CRLSign, (const void **)&kuRef)
228  			&& CFBooleanGetValue(kuRef) == true)
229  			options.IntendedUsage |= CE_KU_CRLSign;
230  		if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_EncipherOnly, (const void **)&kuRef)
231  			&& CFBooleanGetValue(kuRef) == true)
232  			options.IntendedUsage |= CE_KU_EncipherOnly;
233  		if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyKU_DecipherOnly, (const void **)&kuRef)
234  			&& CFBooleanGetValue(kuRef) == true)
235  			options.IntendedUsage |= CE_KU_DecipherOnly;
236  
237  		const CssmData value((uint8*)&options, sizeof(options));
238  		this->setValue(value);
239  
240  		if (buf) free(buf);
241  	}
242  	else if (mOid == CSSMOID_APPLE_TP_REVOCATION)
243  	{
244  		CFNumberRef num = NULL;
245  		if (CFDictionaryGetValueIfPresent(properties, (const void *)kSecPolicyRevocationFlags, (const void **)&num)) {
246  			CFOptionFlags revocationFlags = 0;
247  			if (num) {
248  				(void)CFNumberGetValue(num, kCFNumberCFIndexType, &revocationFlags);
249  			}
250  			const CssmData value((uint8*)&revocationFlags, sizeof(revocationFlags));
251  			this->setValue(value);
252  		}
253  	}
254  
255  }
256  
257  CFDictionaryRef Policy::properties()
258  {
259  	// Builds and returns a dictionary which the caller must release.
260  	CFMutableDictionaryRef properties = CFDictionaryCreateMutable(NULL, 0,
261  		&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
262  	if (!properties) return NULL;
263  
264  	// kSecPolicyOid
265  	CFStringRef oidStr = SecDERItemCopyOIDDecimalRepresentation((uint8*)mOid.data(), mOid.length());
266  	if (oidStr) {
267  		CFDictionarySetValue(properties, (const void *)kSecPolicyOid, (const void *)oidStr);
268  		CFRelease(oidStr);
269  	}
270  
271  	// kSecPolicyName
272  	if (mAuxValue) {
273  		CFStringRef nameStr = CFStringCreateWithBytes(NULL,
274  			(const UInt8 *)reinterpret_cast<char*>(mAuxValue.data()),
275  			(CFIndex)mAuxValue.length(), kCFStringEncodingUTF8, false);
276  		if (nameStr) {
277  			if (mOid == CSSMOID_APPLE_TP_PASSBOOK_SIGNING) {
278  				CFArrayRef strs = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, nameStr, CFSTR("\t"));
279  				if (strs) {
280  					CFIndex count = CFArrayGetCount(strs);
281  					if (count > 0)
282  						CFDictionarySetValue(properties, (const void *)kSecPolicyName, (const void *)CFArrayGetValueAtIndex(strs, 0));
283  					if (count > 1)
284  						CFDictionarySetValue(properties, (const void *)kSecPolicyTeamIdentifier, (const void *)CFArrayGetValueAtIndex(strs, 1));
285  					CFRelease(strs);
286  				}
287  			}
288  			else {
289  				CFDictionarySetValue(properties, (const void *)kSecPolicyName, (const void *)nameStr);
290  			}
291  			CFRelease(nameStr);
292  		}
293  	}
294  
295  	// kSecPolicyClient
296  	if (mValue) {
297  		if (mOid == CSSMOID_APPLE_TP_SSL ||
298  			mOid == CSSMOID_APPLE_TP_EAP ||
299  			mOid == CSSMOID_APPLE_TP_IP_SEC ||
300  			mOid == CSSMOID_APPLE_TP_APPLEID_SHARING)
301  		{
302  			CSSM_APPLE_TP_SSL_OPTIONS *opts = (CSSM_APPLE_TP_SSL_OPTIONS *)mValue.data();
303  			if (opts->Flags & CSSM_APPLE_TP_SSL_CLIENT) {
304  				CFDictionarySetValue(properties, (const void *)kSecPolicyClient, (const void *)kCFBooleanTrue);
305  			}
306  		}
307  	}
308  
309  	// key usage flags (currently only for S/MIME and iChat policies)
310  	if (mValue) {
311  		if (mOid == CSSMOID_APPLE_TP_SMIME ||
312  			mOid == CSSMOID_APPLE_TP_ICHAT)
313  		{
314  			CSSM_APPLE_TP_SMIME_OPTIONS *opts = (CSSM_APPLE_TP_SMIME_OPTIONS *)mValue.data();
315  			CE_KeyUsage usage = opts->IntendedUsage;
316  			if (usage & CE_KU_DigitalSignature)
317  				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_DigitalSignature, (const void *)kCFBooleanTrue);
318  			if (usage & CE_KU_NonRepudiation)
319  				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_NonRepudiation, (const void *)kCFBooleanTrue);
320  			if (usage & CE_KU_KeyEncipherment)
321  				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_KeyEncipherment, (const void *)kCFBooleanTrue);
322  			if (usage & CE_KU_DataEncipherment)
323  				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_DataEncipherment, (const void *)kCFBooleanTrue);
324  			if (usage & CE_KU_KeyAgreement)
325  				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_KeyAgreement, (const void *)kCFBooleanTrue);
326  			if (usage & CE_KU_KeyCertSign)
327  				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_KeyCertSign, (const void *)kCFBooleanTrue);
328  			if (usage & CE_KU_CRLSign)
329  				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_CRLSign, (const void *)kCFBooleanTrue);
330  			if (usage & CE_KU_EncipherOnly)
331  				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_EncipherOnly, (const void *)kCFBooleanTrue);
332  			if (usage & CE_KU_DecipherOnly)
333  				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_DecipherOnly, (const void *)kCFBooleanTrue);
334  		}
335  		else if (mOid == CSSMOID_APPLE_TP_REVOCATION)
336  		{
337  			CFOptionFlags *revocationFlagsPtr = (CFOptionFlags *)mValue.data();
338  			if (revocationFlagsPtr) {
339  				CFNumberRef num = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, revocationFlagsPtr);
340  				if (num) {
341  					CFDictionarySetValue(properties, (const void *)kSecPolicyRevocationFlags, num);
342  					CFRelease(num);
343  				}
344  			}
345  		}
346  	}
347  	return properties;
348  }
349  
350  
351  bool Policy::operator < (const Policy& other) const
352  {
353      //@@@ inefficient
354      return (oid() < other.oid()) ||
355          (oid() == other.oid() && value() < other.value());
356  }
357  
358  bool Policy::operator == (const Policy& other) const
359  {
360      return oid() == other.oid() && value() == other.value();
361  }