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