1/*
2 * Copyright (c) 2002-2004,2011-2014 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
36static
37CFStringRef 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
76using namespace KeychainCore;
77
78Policy::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	secdebug("policy", "Policy() this %p", this);
86}
87
88Policy::~Policy() throw()
89{
90	secdebug("policy", "~Policy() this %p", this);
91}
92
93void 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
147void 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
243}
244
245CFDictionaryRef Policy::properties()
246{
247	// Builds and returns a dictionary which the caller must release.
248	CFMutableDictionaryRef properties = CFDictionaryCreateMutable(NULL, 0,
249		&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
250	if (!properties) return NULL;
251
252	// kSecPolicyOid
253	CFStringRef oidStr = SecDERItemCopyOIDDecimalRepresentation((uint8*)mOid.data(), mOid.length());
254	if (oidStr) {
255		CFDictionarySetValue(properties, (const void *)kSecPolicyOid, (const void *)oidStr);
256		CFRelease(oidStr);
257	}
258
259	// kSecPolicyName
260	if (mAuxValue) {
261		CFStringRef nameStr = CFStringCreateWithBytes(NULL,
262			(const UInt8 *)reinterpret_cast<char*>(mAuxValue.data()),
263			(CFIndex)mAuxValue.length(), kCFStringEncodingUTF8, false);
264		if (nameStr) {
265			if (mOid == CSSMOID_APPLE_TP_PASSBOOK_SIGNING) {
266				CFArrayRef strs = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, nameStr, CFSTR("\t"));
267				if (strs) {
268					CFIndex count = CFArrayGetCount(strs);
269					if (count > 0)
270						CFDictionarySetValue(properties, (const void *)kSecPolicyName, (const void *)CFArrayGetValueAtIndex(strs, 0));
271					if (count > 1)
272						CFDictionarySetValue(properties, (const void *)kSecPolicyTeamIdentifier, (const void *)CFArrayGetValueAtIndex(strs, 1));
273					CFRelease(strs);
274				}
275			}
276			else {
277				CFDictionarySetValue(properties, (const void *)kSecPolicyName, (const void *)nameStr);
278			}
279			CFRelease(nameStr);
280		}
281	}
282
283	// kSecPolicyClient
284	if (mValue) {
285		if (mOid == CSSMOID_APPLE_TP_SSL ||
286			mOid == CSSMOID_APPLE_TP_EAP ||
287			mOid == CSSMOID_APPLE_TP_IP_SEC ||
288			mOid == CSSMOID_APPLE_TP_APPLEID_SHARING)
289		{
290			CSSM_APPLE_TP_SSL_OPTIONS *opts = (CSSM_APPLE_TP_SSL_OPTIONS *)mValue.data();
291			if (opts->Flags & CSSM_APPLE_TP_SSL_CLIENT) {
292				CFDictionarySetValue(properties, (const void *)kSecPolicyClient, (const void *)kCFBooleanTrue);
293			}
294		}
295	}
296
297	// key usage flags (currently only for S/MIME and iChat policies)
298	if (mValue) {
299		if (mOid == CSSMOID_APPLE_TP_SMIME ||
300			mOid == CSSMOID_APPLE_TP_ICHAT)
301		{
302			CSSM_APPLE_TP_SMIME_OPTIONS *opts = (CSSM_APPLE_TP_SMIME_OPTIONS *)mValue.data();
303			CE_KeyUsage usage = opts->IntendedUsage;
304			if (usage & CE_KU_DigitalSignature)
305				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_DigitalSignature, (const void *)kCFBooleanTrue);
306			if (usage & CE_KU_NonRepudiation)
307				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_NonRepudiation, (const void *)kCFBooleanTrue);
308			if (usage & CE_KU_KeyEncipherment)
309				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_KeyEncipherment, (const void *)kCFBooleanTrue);
310			if (usage & CE_KU_DataEncipherment)
311				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_DataEncipherment, (const void *)kCFBooleanTrue);
312			if (usage & CE_KU_KeyAgreement)
313				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_KeyAgreement, (const void *)kCFBooleanTrue);
314			if (usage & CE_KU_KeyCertSign)
315				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_KeyCertSign, (const void *)kCFBooleanTrue);
316			if (usage & CE_KU_CRLSign)
317				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_CRLSign, (const void *)kCFBooleanTrue);
318			if (usage & CE_KU_EncipherOnly)
319				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_EncipherOnly, (const void *)kCFBooleanTrue);
320			if (usage & CE_KU_DecipherOnly)
321				CFDictionarySetValue(properties, (const void *)kSecPolicyKU_DecipherOnly, (const void *)kCFBooleanTrue);
322		}
323	}
324	return properties;
325}
326
327
328bool Policy::operator < (const Policy& other) const
329{
330    //@@@ inefficient
331    return (oid() < other.oid()) ||
332        (oid() == other.oid() && value() < other.value());
333}
334
335bool Policy::operator == (const Policy& other) const
336{
337    return oid() == other.oid() && value() == other.value();
338}
339