1/*
2 * Copyright (c) 2004 Apple Computer, 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// tdtransit - security tokend client library transition code.
27//
28#include "tdtransit.h"
29#include "tokend.h"
30#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
31
32
33namespace Security {
34namespace Tokend {
35
36static void copyDataFromIPC(void *dataPtr, mach_msg_type_number_t dataLength, CssmData *outData);
37
38using MachPlusPlus::VMGuard;
39
40
41void ClientSession::check(kern_return_t rc)
42{
43	switch (rc) {
44	case KERN_SUCCESS:
45		return;
46	case MIG_SERVER_DIED:
47		fault();
48		CssmError::throwMe(CSSM_ERRCODE_DEVICE_FAILED);
49	default:
50		MachPlusPlus::check(rc);
51	}
52}
53
54
55//
56// DataOutput helper.
57// This happens "at the end" of a glue method, via the DataOutput destructor.
58//
59DataOutput::~DataOutput()
60{
61	// @@@ Why are we setting up a VMGuard if mData is NULL?
62	VMGuard _(mData, mLength);
63	if (mData) {	// was assigned to; IPC returned OK
64		if (argument) {	// buffer was provided
65			if (argument.length() < mLength)
66				CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR);
67			argument.length(mLength);
68		} else {	// allocate buffer
69			argument = CssmData(allocator.malloc(mLength), mLength);
70		}
71		memcpy(argument.data(), mData, mLength);
72	}
73}
74
75
76//
77// Create a packaged-up Context for IPC transmission.
78// In addition to collecting the context into a contiguous blob for transmission,
79// we also evaluate CssmCryptoData callbacks at this time.
80//
81SendContext::SendContext(const Security::Context &ctx) : context(ctx)
82{
83	CssmCryptoData cryptoDataValue;	// holding area for CssmCryptoData element
84	IFDEBUG(uint32 cryptoDataUsed = 0);
85	Context::Builder builder(Allocator::standard());
86	for (unsigned n = 0; n < ctx.attributesInUse(); n++) {
87		switch (ctx[n].baseType()) {
88		case CSSM_ATTRIBUTE_DATA_CRYPTO_DATA: {
89			CssmCryptoData &data = ctx[n];	// extract CssmCryptoData value
90			cryptoDataValue = data();		// evaluate callback (if any)
91			builder.setup(&cryptoDataValue); // use evaluted value
92			IFDEBUG(cryptoDataUsed++);
93			break;
94		}
95		default:
96			builder.setup(ctx[n]);
97			break;
98		}
99	}
100	attributeSize = builder.make();
101	for (unsigned n = 0; n < ctx.attributesInUse(); n++) {
102		const Context::Attr &attr = ctx[n];
103		switch (attr.baseType()) {
104		case CSSM_ATTRIBUTE_DATA_CRYPTO_DATA:
105			builder.put(attr.type(), &cryptoDataValue);
106			break;
107		default:
108			builder.put(attr);
109			break;
110		}
111	}
112	uint32 count;	// not needed
113	builder.done(attributes, count);
114	assert(cryptoDataUsed <= 1);	// no more than one slot converted
115}
116
117
118//
119// Setup and basic control
120//
121void ClientSession::probe(Score &score, std::string &tokenUid)
122{
123	char uid[PATH_MAX];
124	IPC(tokend_client_probe(TOKEND_ARGS, &score, uid));
125	tokenUid = uid;
126}
127
128
129void ClientSession::establish(Guid &guid, uint32 ssid,
130	uint32 flags, const char *cacheDirectory, const char *workDirectory,
131	char mdsDirectory[PATH_MAX], char printName[PATH_MAX])
132{
133	IPC(tokend_client_establish(TOKEND_ARGS, guid, ssid,
134		flags, cacheDirectory, workDirectory, mdsDirectory, printName));
135}
136
137
138//
139// The soft kiss-of-death. This is a one-way RPC; no reply is sent or received;
140// the call returns immediately.
141//
142void ClientSession::terminate(uint32 reason, uint32 options)
143{
144	IPC((rcode = CSSM_OK, tokend_client_terminate(mServicePort, reason, options)));
145}
146
147
148//
149// Data search/retrieval interface
150//
151RecordHandle ClientSession::findFirst(const CssmQuery &query,
152	CssmDbRecordAttributeData *inAttributes, size_t inAttributesLength,
153	SearchHandle &hSearch, CssmData *outData, KeyHandle &hKey,
154	CssmDbRecordAttributeData *&outAttributes, mach_msg_type_number_t &outAttributesLength)
155{
156	Copier<CssmQuery> theQuery(&query, internalAllocator);
157	void *dataPtr; mach_msg_type_number_t dataLength;
158	RecordHandle hRecord;
159	CssmDbRecordAttributeData *outAttributesBase;
160	CssmDbRecordAttributeData *tmpOutAttributes = NULL;
161	IPC(tokend_client_findFirst(TOKEND_ARGS, COPY(theQuery), COPYFLAT(inAttributes),
162		outData != NULL, &dataPtr, &dataLength, &hKey,
163		&tmpOutAttributes, &outAttributesLength, &outAttributesBase,
164		&hSearch, &hRecord));
165//	DataOutput out_data(data, returnAllocator);
166	if (outAttributesLength)
167	{
168		outAttributes = static_cast<CssmDbRecordAttributeData *>(malloc(outAttributesLength));
169		memcpy(outAttributes, tmpOutAttributes, outAttributesLength);
170		relocate(outAttributes, outAttributesBase);
171	}
172	else
173		outAttributes = NULL;
174	copyDataFromIPC(dataPtr, dataLength, outData);
175	if (tmpOutAttributes)
176		mig_deallocate(reinterpret_cast<vm_address_t>(tmpOutAttributes), outAttributesLength);
177	return hRecord;
178}
179
180RecordHandle ClientSession::findNext(SearchHandle hSearch,
181	CssmDbRecordAttributeData *inAttributes, size_t inAttributesLength,
182	CssmData *outData, KeyHandle &hKey,
183	CssmDbRecordAttributeData *&outAttributes, mach_msg_type_number_t &outAttributesLength)
184{
185	void *dataPtr; mach_msg_type_number_t dataLength;
186	RecordHandle hRecord;
187	CssmDbRecordAttributeData *outAttributesBase;
188	CssmDbRecordAttributeData *tmpOutAttributes = NULL;
189	IPC(tokend_client_findNext(TOKEND_ARGS, hSearch, COPYFLAT(inAttributes),
190		outData != NULL, &dataPtr, &dataLength, &hKey,
191		&tmpOutAttributes, &outAttributesLength, &outAttributesBase,
192		&hRecord));
193	if (outAttributesLength)
194	{
195		outAttributes = static_cast<CssmDbRecordAttributeData *>(malloc(outAttributesLength));
196		memcpy(outAttributes, tmpOutAttributes, outAttributesLength);
197		relocate(outAttributes, outAttributesBase);
198	}
199	else
200		outAttributes = NULL;
201	copyDataFromIPC(dataPtr, dataLength, outData);
202	if (tmpOutAttributes)
203		mig_deallocate(reinterpret_cast<vm_address_t>(tmpOutAttributes), outAttributesLength);
204	return hRecord;
205}
206
207void ClientSession::findRecordHandle(RecordHandle hRecord,
208	CssmDbRecordAttributeData *inAttributes, size_t inAttributesLength,
209	CssmData *outData, KeyHandle &hKey,
210	CssmDbRecordAttributeData *&outAttributes, mach_msg_type_number_t &outAttributesLength)
211{
212	void *dataPtr; mach_msg_type_number_t dataLength;
213	CssmDbRecordAttributeData *outAttributesBase;
214	CssmDbRecordAttributeData *tmpOutAttributes = NULL;
215	IPC(tokend_client_findRecordHandle(TOKEND_ARGS, hRecord, COPYFLAT(inAttributes),
216		outData != NULL, &dataPtr, &dataLength, &hKey,
217		&tmpOutAttributes, &outAttributesLength, &outAttributesBase));
218	if (outAttributesLength)
219	{
220		outAttributes = static_cast<CssmDbRecordAttributeData *>(malloc(outAttributesLength));
221		memcpy(outAttributes, tmpOutAttributes, outAttributesLength);
222		relocate(outAttributes, outAttributesBase);
223	}
224	else
225		outAttributes = NULL;
226	if (tmpOutAttributes)
227		mig_deallocate(reinterpret_cast<vm_address_t>(tmpOutAttributes), outAttributesLength);
228	copyDataFromIPC(dataPtr, dataLength, outData);
229}
230
231void ClientSession::insertRecord(CSSM_DB_RECORDTYPE recordType,
232	const CssmDbRecordAttributeData *inAttributes, size_t attributesLength,
233	const CssmData &data, RecordHandle &hRecord)
234{
235	CssmDbRecordAttributeData *attributes = const_cast<CssmDbRecordAttributeData*>(inAttributes);
236	IPC(tokend_client_insertRecord(TOKEND_ARGS, recordType, COPYFLAT(attributes),
237		DATA(data), &hRecord));
238}
239
240void ClientSession::modifyRecord(CSSM_DB_RECORDTYPE recordType, RecordHandle &hRecord,
241	const CssmDbRecordAttributeData *inAttributes, size_t attributesLength,
242	const CssmData *data, CSSM_DB_MODIFY_MODE modifyMode)
243{
244	CssmDbRecordAttributeData *attributes = const_cast<CssmDbRecordAttributeData*>(inAttributes);
245	IPC(tokend_client_modifyRecord(TOKEND_ARGS, recordType, &hRecord, COPYFLAT(attributes),
246		data != NULL, OPTIONALDATA(data), modifyMode));
247}
248
249void ClientSession::deleteRecord(RecordHandle hRecord)
250{
251	IPC(tokend_client_deleteRecord(TOKEND_ARGS, hRecord));
252}
253
254void ClientSession::releaseSearch(SearchHandle hSearch)
255{
256	IPC(tokend_client_releaseSearch(TOKEND_ARGS, hSearch));
257}
258
259void ClientSession::releaseRecord(RecordHandle hRecord)
260{
261	IPC(tokend_client_releaseRecord(TOKEND_ARGS, hRecord));
262}
263
264
265//
266// Key management and inquiry
267//
268void ClientSession::releaseKey(KeyHandle hKey)
269{
270	IPC(tokend_client_releaseKey(TOKEND_ARGS, hKey));
271}
272
273void ClientSession::queryKeySizeInBits(KeyHandle hKey, CssmKeySize &result)
274{
275    IPC(tokend_client_queryKeySizeInBits(TOKEND_ARGS, hKey, &result));
276}
277
278
279void ClientSession::getOutputSize(const Context &context, KeyHandle hKey,
280    uint32 inputSize, bool encrypt, uint32 &result)
281{
282	SendContext ctx(context);
283    IPC(tokend_client_getOutputSize(TOKEND_ARGS, CONTEXT(ctx), hKey, inputSize, encrypt, &result));
284}
285
286
287//
288// Signatures and MACs
289//
290void ClientSession::generateSignature(const Context &context, KeyHandle hKey,
291	const CssmData &data, CssmData &signature, CSSM_ALGORITHMS signOnlyAlgorithm)
292{
293	SendContext ctx(context);
294	DataOutput sig(signature, returnAllocator);
295	IPC(tokend_client_generateSignature(TOKEND_ARGS, CONTEXT(ctx), hKey, signOnlyAlgorithm,
296		DATA(data), DATA(sig)));
297}
298
299void ClientSession::verifySignature(const Context &context, KeyHandle hKey,
300	const CssmData &data, const CssmData &signature, CSSM_ALGORITHMS verifyOnlyAlgorithm)
301{
302	SendContext ctx(context);
303	IPC(tokend_client_verifySignature(TOKEND_ARGS, CONTEXT(ctx), hKey, verifyOnlyAlgorithm,
304		DATA(data), DATA(signature)));
305}
306
307
308void ClientSession::generateMac(const Context &context, KeyHandle hKey,
309	const CssmData &data, CssmData &signature)
310{
311	SendContext ctx(context);
312	DataOutput sig(signature, returnAllocator);
313	IPC(tokend_client_generateMac(TOKEND_ARGS, CONTEXT(ctx), hKey,
314		DATA(data), DATA(sig)));
315}
316
317void ClientSession::verifyMac(const Context &context, KeyHandle hKey,
318	const CssmData &data, const CssmData &signature)
319{
320	SendContext ctx(context);
321	IPC(tokend_client_verifyMac(TOKEND_ARGS, CONTEXT(ctx), hKey,
322		DATA(data), DATA(signature)));
323}
324
325
326//
327// Encryption/Decryption
328//
329
330void ClientSession::encrypt(const Context &context, KeyHandle hKey,
331	const CssmData &clear, CssmData &cipher)
332{
333	SendContext ctx(context);
334	DataOutput cipherOut(cipher, returnAllocator);
335	IPC(tokend_client_encrypt(TOKEND_ARGS, CONTEXT(ctx), hKey, DATA(clear), DATA(cipherOut)));
336}
337
338void ClientSession::decrypt(const Context &context, KeyHandle hKey,
339	const CssmData &cipher, CssmData &clear)
340{
341	SendContext ctx(context);
342	DataOutput clearOut(clear, returnAllocator);
343	IPC(tokend_client_decrypt(TOKEND_ARGS, CONTEXT(ctx), hKey, DATA(cipher), DATA(clearOut)));
344}
345
346
347//
348// Key generation
349//
350void ClientSession::generateKey(const Security::Context &context,
351		const AccessCredentials *cred, const AclEntryPrototype *proto,
352		uint32 keyUsage, uint32 keyAttr,
353        KeyHandle &hKey, CssmKey *&key)
354{
355	SendContext ctx(context);
356	Copier<AccessCredentials> creds(cred, internalAllocator);
357	Copier<AclEntryPrototype> owner(proto, internalAllocator);
358	CssmKey *keyBase; mach_msg_type_number_t keyLength;
359	IPC(tokend_client_generateKey(TOKEND_ARGS, CONTEXT(ctx),
360		COPY(creds), COPY(owner), keyUsage, keyAttr, &hKey, COPY_OUT(key)));
361	relocate(key, keyBase);
362}
363
364void ClientSession::generateKey(const Security::Context &context,
365		const AccessCredentials *cred, const AclEntryPrototype *proto,
366		CSSM_KEYUSE pubKeyUsage, CSSM_KEYATTR_FLAGS pubKeyAttr,
367		CSSM_KEYUSE privKeyUsage, CSSM_KEYATTR_FLAGS privKeyAttr,
368		KeyHandle &hPubKey, CssmKey *&pubKey,
369        KeyHandle &hPrivKey, CssmKey *&privKey)
370{
371	SendContext ctx(context);
372	Copier<AccessCredentials> creds(cred, internalAllocator);
373	Copier<AclEntryPrototype> owner(proto, internalAllocator);
374	CssmKey *pubKeyBase; mach_msg_type_number_t pubKeyLength;
375	CssmKey *privKeyBase; mach_msg_type_number_t privKeyLength;
376	IPC(tokend_client_generateKeyPair(TOKEND_ARGS, CONTEXT(ctx),
377		COPY(creds), COPY(owner),
378		pubKeyUsage, pubKeyAttr, privKeyUsage, privKeyAttr,
379		&hPubKey, COPY_OUT(pubKey), &hPrivKey, COPY_OUT(privKey)));
380	relocate(pubKey, pubKeyBase);
381	relocate(privKey, privKeyBase);
382}
383
384
385//
386// Key wrapping and unwrapping
387//
388void ClientSession::wrapKey(const Context &context, const AccessCredentials *cred,
389	KeyHandle hWrappingKey, const CssmKey *wrappingKey,
390	KeyHandle hSubjectKey, const CssmKey *subjectKey,
391	const CssmData &descriptiveData, CssmWrappedKey *&wrappedKey)
392{
393	SendContext ctx(context);
394	Copier<AccessCredentials> creds(cred, internalAllocator);
395	Copier<CssmKey> cWrappingKey(wrappingKey, internalAllocator);
396	Copier<CssmKey> cSubjectKey(subjectKey, internalAllocator);
397	CssmKey *wrappedKeyBase; mach_msg_type_number_t wrappedKeyLength;
398	IPC(tokend_client_wrapKey(TOKEND_ARGS, CONTEXT(ctx), hWrappingKey, COPY(cWrappingKey), COPY(creds),
399		hSubjectKey, COPY(cSubjectKey), DATA(descriptiveData), COPY_OUT(wrappedKey)))
400}
401
402void ClientSession::unwrapKey(const Security::Context &context,
403	const AccessCredentials *cred, const AclEntryPrototype *proto,
404	KeyHandle hWrappingKey, const CssmKey *wrappingKey,
405	KeyHandle hPublicKey, const CssmKey *publicKey,
406	const CssmWrappedKey &wrappedKey, uint32 usage, uint32 attrs,
407	CssmData &descriptiveData, KeyHandle &hKey, CssmKey *&key)
408{
409	SendContext ctx(context);
410	Copier<AccessCredentials> creds(cred, internalAllocator);
411	Copier<AclEntryPrototype> owner(proto, internalAllocator);
412	Copier<CssmKey> cWrappingKey(wrappingKey, internalAllocator);
413	Copier<CssmKey> cPublicKey(publicKey, internalAllocator);
414	Copier<CssmWrappedKey> cWrappedKey(&wrappedKey, internalAllocator);
415	CssmKey *tmpKeyBase; mach_msg_type_number_t tmpKeyLength;
416	CssmKey *tmpKey;
417	DataOutput descriptor(descriptiveData, returnAllocator);
418	IPC(tokend_client_unwrapKey(TOKEND_ARGS, CONTEXT(ctx), hWrappingKey, COPY(cWrappingKey),
419		COPY(creds), COPY(owner), hPublicKey, COPY(cPublicKey),
420		COPY(cWrappedKey), usage, attrs, DATA(descriptor),
421        &hKey, COPY_OUT(tmpKey)));
422	relocate(tmpKey, tmpKeyBase);
423	key = chunkCopy(tmpKey);
424}
425
426
427//
428// Key derivation
429//
430void ClientSession::deriveKey(DbHandle db, const Context &context,
431	KeyHandle hBaseKey, const CssmKey *baseKey,
432    uint32 keyUsage, uint32 keyAttr, CssmData &param,
433    const AccessCredentials *cred, const AclEntryPrototype *proto,
434    KeyHandle &hKey, CssmKey *&key)
435{
436    SendContext ctx(context);
437	Copier<AccessCredentials> creds(cred, internalAllocator);
438	Copier<AclEntryPrototype> owner(proto, internalAllocator);
439	Copier<CssmKey> cBaseKey(baseKey, internalAllocator);
440	CssmDeriveData inForm(param, context.algorithm());
441	Copier<CssmDeriveData> inParam(&inForm, internalAllocator);
442	CssmKey *keyBase; mach_msg_type_number_t keyLength;
443    DataOutput paramOutput(param, returnAllocator);
444	IPC(tokend_client_deriveKey(TOKEND_ARGS, CONTEXT(ctx), hBaseKey, COPY(cBaseKey),
445		COPY(creds), COPY(owner), COPY(inParam), DATA(paramOutput),
446		keyUsage, keyAttr, &hKey, COPY_OUT(key)));
447}
448
449
450//
451// ACL getting/setting interface.
452// Note that this layer, unlike the securityd-client one, does not return its
453// output data as separate allocated (chunked) copies. It returns its data
454// directly as a (relocated) flat blob.
455//
456void ClientSession::getAcl(AclKind kind, GenericHandle key, const char *tag,
457	uint32 &count, AclEntryInfo * &info)
458{
459	AclEntryInfo *infoBase;
460	mach_msg_type_number_t infoLength;
461	IPC(tokend_client_getAcl(TOKEND_ARGS, kind, key,
462		(tag != NULL), tag ? tag : "",
463		&count, COPY_OUT(info)));
464
465	// relocate incoming AclEntryInfo array
466	ReconstituteWalker relocator(info, infoBase);
467	for (uint32 n = 0; n < count; n++)
468		walk(relocator, info[n]);
469}
470
471void ClientSession::changeAcl(AclKind kind, GenericHandle key,
472	const AccessCredentials &cred, const AclEdit &edit)
473{
474	Copier<AccessCredentials> creds(&cred, internalAllocator);
475	Copier<AclEntryInput> infos(edit.newEntry(), internalAllocator);
476	IPC(tokend_client_changeAcl(TOKEND_ARGS, kind, key, COPY(creds),
477		edit.mode(), edit.handle(), COPY(infos)));
478}
479
480void ClientSession::getOwner(AclKind kind, GenericHandle handle, AclOwnerPrototype *&owner)
481{
482	AclOwnerPrototype *ownerBase; mach_msg_type_number_t ownerLength;
483	IPC(tokend_client_getOwner(TOKEND_ARGS, kind, handle, COPY_OUT(owner)));
484	// turn the returned AclOwnerPrototype into its proper output form
485	relocate(owner, ownerBase);
486}
487
488void ClientSession::changeOwner(AclKind kind, GenericHandle key, const AccessCredentials &cred,
489	const AclOwnerPrototype &edit)
490{
491	CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
492}
493
494
495void ClientSession::authenticate(CSSM_DB_ACCESS_TYPE mode, const AccessCredentials *cred)
496{
497	Copier<AccessCredentials> creds(cred, internalAllocator);
498	IPC(tokend_client_authenticate(TOKEND_ARGS, mode, COPY(creds)));
499}
500
501bool ClientSession::isLocked()
502{
503	uint32 locked = 0;
504	IPC(tokend_client_isLocked(TOKEND_ARGS, &locked));
505	return locked;
506}
507
508static void copyDataFromIPC(void *dataPtr, mach_msg_type_number_t dataLength, CssmData *outData)
509{
510	if (!outData || !dataPtr)
511		return;
512
513	/*
514		<rdar://problem/6738709> securityd leaks VM memory during certain smartcard operations
515		The preceeding IPC call to tokend_client_findRecordHandle() vm_allocates()s the memory
516		for returning dataPtr. The rest of the system wants to treat this memory as a CssmData object
517		and call free() on it. But free() won't work for vm_allocate()d memory, so copy the data into
518		heap memory.
519	*/
520
521	void *newData = malloc(dataLength);
522	if (newData == NULL)
523		CssmError::throwMe(CSSM_ERRCODE_MEMORY_ERROR);
524
525	memcpy(newData, dataPtr, dataLength);
526	*outData = CssmData(newData, dataLength);
527
528//	secdebug(who, "dataPtr (%p), dataLength (%d); newData (%p)", dataPtr, dataLength, newData);
529
530	mig_deallocate(reinterpret_cast<vm_address_t>(dataPtr), (vm_size_t)dataLength);
531}
532
533
534}	// namespace Tokend
535}	// namespace Security
536