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// SDCSPSession.cpp - Security Server CSP session.
27//
28#include "SDCSPSession.h"
29
30#include "SDCSPDLPlugin.h"
31#include "SDDLSession.h"
32#include "SDKey.h"
33#include <security_cdsa_utilities/cssmbridge.h>
34#include <memory>
35
36using namespace std;
37using namespace SecurityServer;
38
39//
40// SDCSPSession -- Security Server CSP session
41//
42SDCSPSession::SDCSPSession(CSSM_MODULE_HANDLE handle,
43						   SDCSPDLPlugin &plug,
44						   const CSSM_VERSION &version,
45						   uint32 subserviceId,
46						   CSSM_SERVICE_TYPE subserviceType,
47						   CSSM_ATTACH_FLAGS attachFlags,
48						   const CSSM_UPCALLS &upcalls,
49						   SDCSPDLSession &ssCSPDLSession,
50						   CssmClient::CSP &rawCsp)
51: CSPFullPluginSession(handle, plug, version, subserviceId, subserviceType,
52					   attachFlags, upcalls),
53  mSDCSPDLSession(ssCSPDLSession),
54  mSDFactory(plug.mSDFactory),
55  mRawCsp(rawCsp),
56  mClientSession(Allocator::standard(), *this)
57{
58}
59
60//
61// Called at (CSSM) context create time. This is ignored; we do a full
62// context setup later, at setupContext time.
63//
64CSPFullPluginSession::CSPContext *
65SDCSPSession::contextCreate(CSSM_CC_HANDLE handle, const Context &context)
66{
67	return NULL;
68}
69
70
71//
72// Called by CSPFullPluginSession when an op is actually commencing.
73// Context can safely assumed to be fully formed and stable for the
74// duration of the  op; thus we wait until now to set up our
75// CSPContext as appropriate to the op.
76//
77void
78SDCSPSession::setupContext(CSPContext * &cspCtx,
79						   const Context &context,
80						   bool encoding)
81{
82	// note we skip this if this CSPContext is being reused
83    if (cspCtx == NULL)
84	{
85
86		if (mSDFactory.setup(*this, cspCtx, context, encoding))
87			return;
88
89#if 0
90		if (mBSafe4Factory.setup(*this, cspCtx, context))
91			return;
92
93		if (mCryptKitFactory.setup(*this, cspCtx, context))
94			return;
95#endif
96
97        CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
98	}
99}
100
101
102//
103// DL interaction
104//
105CSSM_DB_HANDLE
106SDCSPSession::getDatabase(const Context &context)
107{
108	return getDatabase(context.get<CSSM_DL_DB_HANDLE>(CSSM_ATTRIBUTE_DL_DB_HANDLE));
109}
110
111CSSM_DB_HANDLE
112SDCSPSession::getDatabase(CSSM_DL_DB_HANDLE *aDLDbHandle)
113{
114	if (aDLDbHandle)
115		return aDLDbHandle->DBHandle;
116	else
117		return CSSM_INVALID_HANDLE;
118}
119
120
121//
122// Reference Key management
123//
124void
125SDCSPSession::makeReferenceKey(KeyHandle inKeyHandle, CssmKey &ioKey, CSSM_DB_HANDLE inDBHandle,
126							   uint32 inKeyAttr, const CssmData *inKeyLabel)
127{
128	return mSDCSPDLSession.makeReferenceKey(*this, inKeyHandle, ioKey, inDBHandle, inKeyAttr, inKeyLabel);
129}
130
131SDKey &
132SDCSPSession::lookupKey(const CssmKey &inKey)
133{
134	return mSDCSPDLSession.lookupKey(inKey);
135}
136
137
138//
139// Key creating and handeling members
140//
141void
142SDCSPSession::WrapKey(CSSM_CC_HANDLE CCHandle,
143					  const Context &context,
144					  const AccessCredentials &AccessCred,
145					  const CssmKey &Key,
146					  const CssmData *DescriptiveData,
147					  CssmKey &WrappedKey,
148					  CSSM_PRIVILEGE Privilege)
149{
150	// @@@ Deal with permanent keys
151 	const CssmKey *keyInContext =
152		context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY);
153
154	KeyHandle contextKeyHandle = (keyInContext
155								  ? lookupKey(*keyInContext).keyHandle()
156								  : noKey);
157	clientSession().wrapKey(context, contextKeyHandle,
158							lookupKey(Key).keyHandle(), &AccessCred,
159							DescriptiveData, WrappedKey, *this);
160}
161
162void
163SDCSPSession::UnwrapKey(CSSM_CC_HANDLE CCHandle,
164						const Context &context,
165						const CssmKey *PublicKey,
166						const CssmWrappedKey &WrappedKey,
167						uint32 KeyUsage,
168						uint32 KeyAttr,
169						const CssmData *KeyLabel,
170						const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry,
171						CssmKey &UnwrappedKey,
172						CssmData &DescriptiveData,
173						CSSM_PRIVILEGE Privilege)
174{
175	CSSM_DB_HANDLE database = getDatabase(context);
176	validateKeyAttr(KeyAttr);
177	const AccessCredentials *cred = NULL;
178	const AclEntryInput *owner = NULL;
179	if (CredAndAclEntry)
180	{
181		cred = AccessCredentials::overlay(CredAndAclEntry->AccessCred);
182		owner = &AclEntryInput::overlay(CredAndAclEntry->InitialAclEntry);
183	}
184
185	KeyHandle publicKey = noKey;
186	if (PublicKey)
187	{
188		if (PublicKey->blobType() == CSSM_KEYBLOB_RAW)
189		{
190			// @@@ We need to unwrap the publicKey into the SecurityServer
191			// before continuing
192			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
193		}
194		else
195			publicKey = lookupKey(*PublicKey).keyHandle();
196	}
197
198	// @@@ Deal with permanent keys
199 	const CssmKey *keyInContext =
200		context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY);
201
202	KeyHandle contextKeyHandle =
203		keyInContext ? lookupKey(*keyInContext).keyHandle() : noKey;
204
205	KeyHandle unwrappedKeyHandle;
206	clientSession().unwrapKey(ClientSession::toIPCHandle(database), context, contextKeyHandle,
207							  publicKey, WrappedKey, KeyUsage, KeyAttr,
208							  cred, owner, DescriptiveData, unwrappedKeyHandle,
209							  UnwrappedKey.header(), *this);
210	makeReferenceKey(unwrappedKeyHandle, UnwrappedKey, database, KeyAttr,
211					 KeyLabel);
212}
213
214void
215SDCSPSession::DeriveKey(CSSM_CC_HANDLE ccHandle,
216						const Context &context,
217						CssmData &param,
218						uint32 keyUsage,
219						uint32 keyAttr,
220						const CssmData *keyLabel,
221						const CSSM_RESOURCE_CONTROL_CONTEXT *credAndAclEntry,
222						CssmKey &derivedKey)
223{
224	CSSM_DB_HANDLE database = getDatabase(context);
225	validateKeyAttr(keyAttr);
226	const AccessCredentials *cred = NULL;
227	const AclEntryInput *owner = NULL;
228	if (credAndAclEntry)
229	{
230		cred = AccessCredentials::overlay(credAndAclEntry->AccessCred);
231		owner = &AclEntryInput::overlay(credAndAclEntry->InitialAclEntry);
232	}
233
234	/* optional BaseKey */
235 	const CssmKey *keyInContext =
236		context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY);
237	KeyHandle contextKeyHandle =
238		keyInContext ? lookupKey(*keyInContext).keyHandle() : noKey;
239	KeyHandle keyHandle;
240	switch(context.algorithm()) {
241	case CSSM_ALGID_KEYCHAIN_KEY:
242		{
243			// special interpretation: take DLDBHandle -> DbHandle from params
244			clientSession().extractMasterKey(ClientSession::toIPCHandle(database), context,
245				(DbHandle)getDatabase(param.interpretedAs<CSSM_DL_DB_HANDLE>(CSSMERR_CSP_INVALID_ATTR_DL_DB_HANDLE)),
246				keyUsage, keyAttr, cred, owner, keyHandle, derivedKey.header());
247		}
248		break;
249	default:
250            clientSession().deriveKey(ClientSession::toIPCHandle(database), context, contextKeyHandle, keyUsage,
251					keyAttr, param, cred, owner, keyHandle, derivedKey.header());
252		break;
253	}
254	makeReferenceKey(keyHandle, derivedKey, database, keyAttr, keyLabel);
255}
256
257void
258SDCSPSession::GenerateKey(CSSM_CC_HANDLE ccHandle,
259						  const Context &context,
260						  uint32 keyUsage,
261						  uint32 keyAttr,
262						  const CssmData *keyLabel,
263						  const CSSM_RESOURCE_CONTROL_CONTEXT *credAndAclEntry,
264						  CssmKey &key,
265						  CSSM_PRIVILEGE privilege)
266{
267	CSSM_DB_HANDLE database = getDatabase(context);
268	validateKeyAttr(keyAttr);
269	const AccessCredentials *cred = NULL;
270	const AclEntryInput *owner = NULL;
271	if (credAndAclEntry)
272	{
273		cred = AccessCredentials::overlay(credAndAclEntry->AccessCred);
274		owner = &AclEntryInput::overlay(credAndAclEntry->InitialAclEntry);
275	}
276
277	KeyHandle keyHandle;
278	clientSession().generateKey(ClientSession::toIPCHandle(database), context, keyUsage,
279								keyAttr, cred, owner, keyHandle, key.header());
280	makeReferenceKey(keyHandle, key, database, keyAttr, keyLabel);
281}
282
283void
284SDCSPSession::GenerateKeyPair(CSSM_CC_HANDLE ccHandle,
285							  const Context &context,
286							  uint32 publicKeyUsage,
287							  uint32 publicKeyAttr,
288							  const CssmData *publicKeyLabel,
289							  CssmKey &publicKey,
290							  uint32 privateKeyUsage,
291							  uint32 privateKeyAttr,
292							  const CssmData *privateKeyLabel,
293							  const CSSM_RESOURCE_CONTROL_CONTEXT *credAndAclEntry,
294							  CssmKey &privateKey,
295							  CSSM_PRIVILEGE privilege)
296{
297	CSSM_DB_HANDLE database = getDatabase(context);
298	validateKeyAttr(publicKeyAttr);
299	validateKeyAttr(privateKeyAttr);
300	const AccessCredentials *cred = NULL;
301	const AclEntryInput *owner = NULL;
302	if (credAndAclEntry)
303	{
304		cred = AccessCredentials::overlay(credAndAclEntry->AccessCred);
305		owner = &AclEntryInput::overlay(credAndAclEntry->InitialAclEntry);
306	}
307
308	/*
309	 * Public keys must be extractable in the clear - that's the Apple
310	 * policy. The raw CSP is unable to enforce the extractable
311	 * bit since it always sees that as true (it's managed and forced
312	 * true by the SecurityServer). So...
313	 */
314	if(!(publicKeyAttr & CSSM_KEYATTR_EXTRACTABLE)) {
315			CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
316	}
317	KeyHandle pubKeyHandle, privKeyHandle;
318	clientSession().generateKey(ClientSession::toIPCHandle(database), context,
319							   publicKeyUsage, publicKeyAttr,
320							   privateKeyUsage, privateKeyAttr,
321							   cred, owner,
322							   pubKeyHandle, publicKey.header(),
323							   privKeyHandle, privateKey.header());
324	makeReferenceKey(privKeyHandle, privateKey, database, privateKeyAttr,
325					 privateKeyLabel);
326	// @@@ What if this throws, we need to free privateKey.
327	makeReferenceKey(pubKeyHandle, publicKey, database, publicKeyAttr,
328					 publicKeyLabel);
329}
330
331void
332SDCSPSession::ObtainPrivateKeyFromPublicKey(const CssmKey &PublicKey,
333											CssmKey &PrivateKey)
334{
335	unimplemented();
336}
337
338void
339SDCSPSession::QueryKeySizeInBits(CSSM_CC_HANDLE CCHandle,
340								 const Context &Context,
341								 const CssmKey &Key,
342								 CSSM_KEY_SIZE &KeySize)
343{
344	unimplemented();
345}
346
347void
348SDCSPSession::FreeKey(const AccessCredentials *accessCred,
349					  CssmKey &ioKey, CSSM_BOOL deleteKey)
350{
351	if (ioKey.blobType() == CSSM_KEYBLOB_REFERENCE)
352	{
353		// @@@ Note that this means that detaching a session should free
354		// all keys ascociated with it or else...
355		// -- or else what?
356		// exactly!
357
358		// @@@ There are thread safety issues when deleting a key that is
359		// in use by another thread, but the answer to that is: Don't do
360		// that!
361
362		// Find the key in the map.  Tell tell the key to free itself
363		// (when the auto_ptr deletes the key it removes itself from the map).
364	    secdebug("freeKey", "CSPDL FreeKey");
365		auto_ptr<SDKey> ssKey(&mSDCSPDLSession.find<SDKey>(ioKey));
366		ssKey->free(accessCred, ioKey, deleteKey);
367	}
368	else
369	{
370		CSPFullPluginSession::FreeKey(accessCred, ioKey, deleteKey);
371	}
372}
373
374
375//
376// Generation stuff.
377//
378void
379SDCSPSession::GenerateRandom(CSSM_CC_HANDLE ccHandle,
380							 const Context &context,
381							 CssmData &randomNumber)
382{
383    checkOperation(context.type(), CSSM_ALGCLASS_RANDOMGEN);
384	// if (context.algorithm() != @@@) CssmError::throwMe(ALGORITHM_NOT_SUPPORTED);
385	uint32 needed = context.getInt(CSSM_ATTRIBUTE_OUTPUT_SIZE, CSSMERR_CSP_MISSING_ATTR_OUTPUT_SIZE);
386
387	// @@@ What about the seed?
388    if (randomNumber.length())
389	{
390        if (randomNumber.length() < needed)
391            CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR);
392		clientSession().generateRandom(context, randomNumber);
393    }
394	else
395	{
396        randomNumber.Data = alloc<uint8>(needed);
397		try
398		{
399			clientSession().generateRandom(context, randomNumber);
400		}
401		catch(...)
402		{
403			free(randomNumber.Data);
404			randomNumber.Data = NULL;
405			throw;
406		}
407	}
408}
409
410//
411// Login/Logout and token operational maintainance.  These mean little
412// without support by the actual implementation, but we can help...
413// @@@ Should this be in CSP[non-Full]PluginSession?
414//
415void
416SDCSPSession::Login(const AccessCredentials &AccessCred,
417					const CssmData *LoginName,
418					const void *Reserved)
419{
420	// @@@ Do a login to the securityServer making keys persistant until it
421	// goes away
422	unimplemented();
423}
424
425void
426SDCSPSession::Logout()
427{
428	unimplemented();
429}
430
431void
432SDCSPSession::VerifyDevice(const CssmData &DeviceCert)
433{
434	CssmError::throwMe(CSSMERR_CSP_DEVICE_VERIFY_FAILED);
435}
436
437void
438SDCSPSession::GetOperationalStatistics(CSPOperationalStatistics &statistics)
439{
440	unimplemented();
441}
442
443
444//
445// Utterly miscellaneous, rarely used, strange functions
446//
447void
448SDCSPSession::RetrieveCounter(CssmData &Counter)
449{
450	unimplemented();
451}
452
453void
454SDCSPSession::RetrieveUniqueId(CssmData &UniqueID)
455{
456	unimplemented();
457}
458
459void
460SDCSPSession::GetTimeValue(CSSM_ALGORITHMS TimeAlgorithm, CssmData &TimeData)
461{
462	unimplemented();
463}
464
465
466//
467// ACL retrieval and change operations
468//
469void
470SDCSPSession::GetKeyOwner(const CssmKey &Key,
471						  CSSM_ACL_OWNER_PROTOTYPE &Owner)
472{
473	lookupKey(Key).getOwner(Owner, *this);
474}
475
476void
477SDCSPSession::ChangeKeyOwner(const AccessCredentials &AccessCred,
478							 const CssmKey &Key,
479							 const CSSM_ACL_OWNER_PROTOTYPE &NewOwner)
480{
481	lookupKey(Key).changeOwner(AccessCred,
482							   AclOwnerPrototype::overlay(NewOwner));
483}
484
485void
486SDCSPSession::GetKeyAcl(const CssmKey &Key,
487						const CSSM_STRING *SelectionTag,
488						uint32 &NumberOfAclInfos,
489						CSSM_ACL_ENTRY_INFO_PTR &AclInfos)
490{
491	lookupKey(Key).getAcl(reinterpret_cast<const char *>(SelectionTag),
492						  NumberOfAclInfos,
493						  reinterpret_cast<AclEntryInfo *&>(AclInfos), *this);
494}
495
496void
497SDCSPSession::ChangeKeyAcl(const AccessCredentials &AccessCred,
498						   const CSSM_ACL_EDIT &AclEdit,
499						   const CssmKey &Key)
500{
501	lookupKey(Key).changeAcl(AccessCred, AclEdit::overlay(AclEdit));
502}
503
504void
505SDCSPSession::GetLoginOwner(CSSM_ACL_OWNER_PROTOTYPE &Owner)
506{
507	unimplemented();
508}
509
510void
511SDCSPSession::ChangeLoginOwner(const AccessCredentials &AccessCred,
512							   const CSSM_ACL_OWNER_PROTOTYPE &NewOwner)
513{
514	unimplemented();
515}
516
517void
518SDCSPSession::GetLoginAcl(const CSSM_STRING *SelectionTag,
519						  uint32 &NumberOfAclInfos,
520						  CSSM_ACL_ENTRY_INFO_PTR &AclInfos)
521{
522	unimplemented();
523}
524
525void
526SDCSPSession::ChangeLoginAcl(const AccessCredentials &AccessCred,
527							 const CSSM_ACL_EDIT &AclEdit)
528{
529	unimplemented();
530}
531
532
533
534//
535// Passthroughs
536//
537void
538SDCSPSession::PassThrough(CSSM_CC_HANDLE CCHandle,
539						  const Context &context,
540						  uint32 passThroughId,
541						  const void *inData,
542						  void **outData)
543{
544    checkOperation(context.type(), CSSM_ALGCLASS_NONE);
545	switch (passThroughId) {
546	case CSSM_APPLESCPDL_CSP_GET_KEYHANDLE:
547	{
548		// inData unused, must be NULL
549		if (inData)
550			CssmError::throwMe(CSSM_ERRCODE_INVALID_INPUT_POINTER);
551
552		// outData required, must be pointer-to-pointer-to-KeyHandle
553		KeyHandle &result = Required(reinterpret_cast<KeyHandle *>(outData));
554
555		// we'll take the key from the context
556		const CssmKey &key =
557			context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY,  CSSMERR_CSP_MISSING_ATTR_KEY);
558
559		// all ready
560		result = lookupKey(key).keyHandle();
561		break;
562	}
563	case CSSM_APPLECSP_KEYDIGEST:
564	{
565		// inData unused, must be NULL
566		if (inData)
567			CssmError::throwMe(CSSM_ERRCODE_INVALID_INPUT_POINTER);
568
569		// outData required
570		Required(outData);
571
572		// take the key from the context, convert to KeyHandle
573		const CssmKey &key =
574			context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY,  CSSMERR_CSP_MISSING_ATTR_KEY);
575		KeyHandle keyHandle = lookupKey(key).keyHandle();
576
577		// allocate digest holder on app's behalf
578		CSSM_DATA *digest = alloc<CSSM_DATA>(sizeof(CSSM_DATA));
579		digest->Data = NULL;
580		digest->Length = 0;
581
582		// go
583		try {
584			clientSession().getKeyDigest(keyHandle, CssmData::overlay(*digest));
585		}
586		catch(...) {
587			free(digest);
588			throw;
589		}
590		*outData = digest;
591		break;
592	}
593
594	default:
595		CssmError::throwMe(CSSM_ERRCODE_INVALID_PASSTHROUGH_ID);
596	}
597}
598
599/* Validate requested key attr flags for newly generated keys */
600void SDCSPSession::validateKeyAttr(uint32 reqKeyAttr)
601{
602	if(reqKeyAttr & (CSSM_KEYATTR_RETURN_DATA)) {
603		/* CSPDL only supports reference keys */
604		CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYATTR_MASK);
605	}
606	if(reqKeyAttr & (CSSM_KEYATTR_ALWAYS_SENSITIVE |
607				     CSSM_KEYATTR_NEVER_EXTRACTABLE)) {
608		/* invalid for any CSP */
609		CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
610	}
611	/* There may be more, but we'll leave it to SS and CSP to decide */
612}
613