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