1/*
2 * Copyright (c) 2000-2001,2008,2011-2012 Apple Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19//
20// SSKey - reference keys for the security server
21//
22#include "SSKey.h"
23
24#include "SSCSPSession.h"
25#include "SSCSPDLSession.h"
26#include "SSDatabase.h"
27#include "SSDLSession.h"
28#include <security_cdsa_utilities/KeySchema.h>
29#include <security_cdsa_plugin/cssmplugin.h>
30
31using namespace CssmClient;
32using namespace SecurityServer;
33
34// Constructor for a Security Server generated key.
35SSKey::SSKey(SSCSPSession &session, KeyHandle keyHandle, CssmKey &ioKey,
36			 SSDatabase &inSSDatabase, uint32 inKeyAttr,
37			 const CssmData *inKeyLabel)
38: ReferencedKey(session.mSSCSPDLSession),
39mAllocator(session), mKeyHandle(keyHandle),
40mClientSession(session.clientSession())
41{
42	CssmKey::Header &header = ioKey.header();
43	if (inKeyAttr & CSSM_KEYATTR_PERMANENT)
44	{
45		if (!inSSDatabase)
46			CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_DL_DB_HANDLE);
47
48		// EncodeKey and store it in the db.
49		CssmDataContainer blob(mAllocator);
50		clientSession().encodeKey(keyHandle, blob);
51
52		assert(header.HeaderVersion == CSSM_KEYHEADER_VERSION);
53		switch (header.KeyClass)
54		{
55		case CSSM_KEYCLASS_PUBLIC_KEY:
56			mRecordType = CSSM_DL_DB_RECORD_PUBLIC_KEY;
57			break;
58		case CSSM_KEYCLASS_PRIVATE_KEY:
59			mRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
60			break;
61		case CSSM_KEYCLASS_SESSION_KEY:
62			mRecordType = CSSM_DL_DB_RECORD_SYMMETRIC_KEY;
63			break;
64		default:
65			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
66		}
67
68		CssmData label;
69		if (inKeyLabel)
70			label = *inKeyLabel;
71
72		CssmData none;
73		// We store the keys real CSP guid on disk
74		CssmGuidData creatorGuid(header.CspId);
75		CssmDateData startDate(header.StartDate);
76		CssmDateData endDate(header.EndDate);
77
78		DbAttributes attributes(inSSDatabase);
79		attributes.recordType(mRecordType);
80		attributes.add(KeySchema::KeyClass, mRecordType);
81		attributes.add(KeySchema::PrintName, label);
82		attributes.add(KeySchema::Alias, none);
83		attributes.add(KeySchema::Permanent,
84					   header.attribute(CSSM_KEYATTR_PERMANENT));
85		attributes.add(KeySchema::Private,
86					   header.attribute(CSSM_KEYATTR_PRIVATE));
87		attributes.add(KeySchema::Modifiable,
88					   header.attribute(CSSM_KEYATTR_MODIFIABLE));
89		attributes.add(KeySchema::Label, label);
90		attributes.add(KeySchema::ApplicationTag, none);
91		attributes.add(KeySchema::KeyCreator, creatorGuid);
92		attributes.add(KeySchema::KeyType, header.AlgorithmId);
93		attributes.add(KeySchema::KeySizeInBits, header.LogicalKeySizeInBits);
94		// @@@ Get the real effective key size.
95		attributes.add(KeySchema::EffectiveKeySize, header.LogicalKeySizeInBits);
96		attributes.add(KeySchema::StartDate, startDate);
97		attributes.add(KeySchema::EndDate, endDate);
98		attributes.add(KeySchema::Sensitive,
99					   header.attribute(CSSM_KEYATTR_SENSITIVE));
100		attributes.add(KeySchema::AlwaysSensitive,
101					   header.attribute(CSSM_KEYATTR_ALWAYS_SENSITIVE));
102		attributes.add(KeySchema::Extractable,
103					   header.attribute(CSSM_KEYATTR_EXTRACTABLE));
104		attributes.add(KeySchema::NeverExtractable,
105					   header.attribute(CSSM_KEYATTR_NEVER_EXTRACTABLE));
106		attributes.add(KeySchema::Encrypt,
107					   header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_ENCRYPT));
108		attributes.add(KeySchema::Decrypt,
109					   header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_DECRYPT));
110		attributes.add(KeySchema::Derive,
111					   header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_DERIVE));
112		attributes.add(KeySchema::Sign,
113					   header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_SIGN));
114		attributes.add(KeySchema::Verify,
115					   header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_VERIFY));
116		attributes.add(KeySchema::SignRecover,
117					   header.useFor(CSSM_KEYUSE_ANY
118									 | CSSM_KEYUSE_SIGN_RECOVER));
119		attributes.add(KeySchema::VerifyRecover,
120					   header.useFor(CSSM_KEYUSE_ANY
121									 | CSSM_KEYUSE_VERIFY_RECOVER));
122		attributes.add(KeySchema::Wrap,
123					   header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_WRAP));
124		attributes.add(KeySchema::Unwrap,
125					   header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_UNWRAP));
126
127		// @@@ Fixme
128		mUniqueId = inSSDatabase->insert(mRecordType, &attributes, &blob,
129										 true);
130	}
131
132	header.cspGuid(session.plugin.myGuid()); // Set the csp guid to me.
133	makeReferenceKey(mAllocator, keyReference(), ioKey);
134}
135
136// Constructor for a key retrived from a Db.
137SSKey::SSKey(SSDLSession &session, CssmKey &ioKey, SSDatabase &inSSDatabase,
138			 const SSUniqueRecord &uniqueId, CSSM_DB_RECORDTYPE recordType,
139			 CssmData &keyBlob)
140: ReferencedKey(session.mSSCSPDLSession),
141mAllocator(session.allocator()), mKeyHandle(noKey), mUniqueId(uniqueId),
142mRecordType(recordType),
143mClientSession(session.clientSession())
144{
145	CssmKey::Header &header = ioKey.header();
146	memset(&header, 0, sizeof(header)); // Clear key header
147
148	if (!mUniqueId || !mUniqueId->database())
149		CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
150
151	header.HeaderVersion = CSSM_KEYHEADER_VERSION;
152	switch (mRecordType)
153	{
154	case CSSM_DL_DB_RECORD_PUBLIC_KEY:
155		header.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY;
156		break;
157	case CSSM_DL_DB_RECORD_PRIVATE_KEY:
158		header.KeyClass = CSSM_KEYCLASS_PRIVATE_KEY;
159		break;
160	case CSSM_DL_DB_RECORD_SYMMETRIC_KEY:
161		header.KeyClass = CSSM_KEYCLASS_SESSION_KEY;
162		break;
163	default:
164		CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
165	}
166
167	DbAttributes attributes(mUniqueId->database());
168	attributes.recordType(mRecordType);
169	attributes.add(KeySchema::KeyClass);			// 0
170	attributes.add(KeySchema::Permanent);			// 1
171	attributes.add(KeySchema::Private);			// 2
172	attributes.add(KeySchema::Modifiable);			// 3
173	attributes.add(KeySchema::KeyCreator);			// 4
174	attributes.add(KeySchema::KeyType);			// 5
175	attributes.add(KeySchema::KeySizeInBits);		// 6
176	attributes.add(KeySchema::StartDate);			// 7
177	attributes.add(KeySchema::EndDate);			// 8
178	attributes.add(KeySchema::Sensitive);			// 9
179	attributes.add(KeySchema::AlwaysSensitive);	// 10
180	attributes.add(KeySchema::Extractable);		// 11
181	attributes.add(KeySchema::NeverExtractable);	// 12
182	attributes.add(KeySchema::Encrypt);			// 13
183	attributes.add(KeySchema::Decrypt);			// 14
184	attributes.add(KeySchema::Derive);				// 15
185	attributes.add(KeySchema::Sign);				// 16
186	attributes.add(KeySchema::Verify);				// 17
187	attributes.add(KeySchema::SignRecover);		// 18
188	attributes.add(KeySchema::VerifyRecover);		// 19
189	attributes.add(KeySchema::Wrap);				// 20
190	attributes.add(KeySchema::Unwrap);				// 21
191
192	mUniqueId->get(&attributes, NULL);
193
194	// Assert that the mRecordType matches the KeyClass attribute.
195	if (mRecordType != uint32(attributes[0]))
196		CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
197
198	header.AlgorithmId = attributes[5]; // KeyType
199    header.LogicalKeySizeInBits = attributes[6]; // KeySizeInBits
200
201	if (attributes[1]) header.setAttribute(CSSM_KEYATTR_PERMANENT);
202	if (attributes[2]) header.setAttribute(CSSM_KEYATTR_PRIVATE);
203	if (attributes[3]) header.setAttribute(CSSM_KEYATTR_MODIFIABLE);
204	if (attributes[9]) header.setAttribute(CSSM_KEYATTR_SENSITIVE);
205	if (attributes[11]) header.setAttribute(CSSM_KEYATTR_EXTRACTABLE);
206	if (attributes[10]) header.setAttribute(CSSM_KEYATTR_ALWAYS_SENSITIVE);
207	if (attributes[12]) header.setAttribute(CSSM_KEYATTR_NEVER_EXTRACTABLE);
208
209	if (attributes[13]) header.usage(CSSM_KEYUSE_ENCRYPT);
210	if (attributes[14]) header.usage(CSSM_KEYUSE_DECRYPT);
211	if (attributes[15]) header.usage(CSSM_KEYUSE_DERIVE);
212	if (attributes[16]) header.usage(CSSM_KEYUSE_SIGN);
213	if (attributes[17]) header.usage(CSSM_KEYUSE_VERIFY);
214	if (attributes[18]) header.usage(CSSM_KEYUSE_SIGN_RECOVER);
215	if (attributes[19]) header.usage(CSSM_KEYUSE_VERIFY_RECOVER);
216	if (attributes[20]) header.usage(CSSM_KEYUSE_WRAP);
217	if (attributes[21]) header.usage(CSSM_KEYUSE_UNWRAP);
218
219	// If all usages are allowed set usage to CSSM_KEYUSE_ANY
220	if (header.usage() == (CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT
221						   | CSSM_KEYUSE_DERIVE | CSSM_KEYUSE_SIGN
222						   | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_SIGN_RECOVER
223						   | CSSM_KEYUSE_VERIFY_RECOVER | CSSM_KEYUSE_WRAP
224						   | CSSM_KEYUSE_UNWRAP))
225		header.usage(CSSM_KEYUSE_ANY);
226
227	if (!attributes[7].size() || !attributes[8].size())
228		CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
229
230    header.StartDate = attributes[7].at<CSSM_DATE>(0);
231    header.EndDate = attributes[8].at<CSSM_DATE>(0);
232
233	makeReferenceKey(mAllocator, keyReference(), ioKey);
234	header.cspGuid(session.plugin.myGuid()); // Set the csp guid to me.
235}
236
237SSKey::~SSKey()
238{
239	if (mKeyHandle != noKey)
240		clientSession().releaseKey(mKeyHandle);
241}
242
243void
244SSKey::free(const AccessCredentials *accessCred, CssmKey &ioKey,
245			CSSM_BOOL deleteKey)
246{
247	freeReferenceKey(mAllocator, ioKey);
248	if (deleteKey)
249	{
250		if (!mUniqueId || !mUniqueId->database())
251			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
252
253		// @@@ Evaluate accessCred against Db acl.
254		// What should we do with accessCred?  Reauthenticate
255		// mUniqueId->database()?
256		mUniqueId->deleteRecord();
257	}
258
259	if (mKeyHandle != noKey)
260	{
261		clientSession().releaseKey(mKeyHandle);
262		mKeyHandle = noKey;
263	}
264}
265
266SecurityServer::ClientSession &
267SSKey::clientSession()
268{
269	return mClientSession;
270}
271
272KeyHandle SSKey::optionalKeyHandle() const
273{
274	return mKeyHandle;
275}
276
277KeyHandle
278SSKey::keyHandle()
279{
280	if (mKeyHandle == noKey)
281	{
282		// Deal with uninstantiated keys.
283		if (!mUniqueId || !mUniqueId->database())
284			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
285
286		CssmDataContainer blob(mAllocator);
287		mUniqueId->get(NULL, &blob);
288		CssmKey::Header dummyHeader; // @@@ Unused
289		mKeyHandle =
290			clientSession().decodeKey(mUniqueId->database().dbHandle(), blob,
291									  dummyHeader);
292
293		// @@@ Check decoded header against returned header
294	}
295
296	return  mKeyHandle;
297}
298
299//
300// ACL retrieval and change operations
301//
302void
303SSKey::getOwner(CSSM_ACL_OWNER_PROTOTYPE &owner, Allocator &allocator)
304{
305	clientSession().getKeyOwner(keyHandle(), AclOwnerPrototype::overlay(owner),
306								allocator);
307}
308
309void
310SSKey::changeOwner(const AccessCredentials &accessCred,
311				   const AclOwnerPrototype &newOwner)
312{
313	clientSession().changeKeyOwner(keyHandle(), accessCred, newOwner);
314	didChangeAcl();
315}
316
317void
318SSKey::getAcl(const char *selectionTag, uint32 &numberOfAclInfos,
319			  AclEntryInfo *&aclInfos, Allocator &allocator)
320{
321	clientSession().getKeyAcl(keyHandle(), selectionTag, numberOfAclInfos,
322							  aclInfos, allocator);
323}
324
325void
326SSKey::changeAcl(const AccessCredentials &accessCred, const AclEdit &aclEdit)
327{
328	clientSession().changeKeyAcl(keyHandle(), accessCred, aclEdit);
329	didChangeAcl();
330}
331
332void
333SSKey::didChangeAcl()
334{
335	if (mUniqueId == true)
336	{
337	    secdebug("keyacl", "SSKey::didChangeAcl() keyHandle: %lu updating DL entry", (unsigned long)mKeyHandle);
338		// The key is persistent, make the change on disk.
339		CssmDataContainer keyBlob(mAllocator);
340		clientSession().encodeKey(keyHandle(), keyBlob);
341		mUniqueId->modify(mRecordType, NULL, &keyBlob, CSSM_DB_MODIFY_ATTRIBUTE_NONE);
342	}
343	else
344	{
345	    secdebug("keyacl", "SSKey::didChangeAcl() keyHandle: %lu transient key no update done", (unsigned long)mKeyHandle);
346	}
347}
348