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 ¶m, 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