createFVMaster.c
1 /* 2 * Copyright (c) 2011-2012,2014 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 * createFVMaster.c 24 */ 25 26 #include "createFVMaster.h" 27 28 #include "readline_cssm.h" 29 #include "security_tool.h" 30 31 #include <pwd.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 #include <fcntl.h> 37 38 #include <Security/SecKeychain.h> 39 #include <Security/SecCertificate.h> 40 #include <Security/SecKeychain.h> 41 #include <Security/oidsalg.h> 42 #include <Security/oidsattr.h> 43 #include <limits.h> 44 #include <utilities/SecCFRelease.h> 45 46 #include "srCdsaUtils.h" 47 48 #ifdef DARLING 49 #include <CarbonCore/MacErrors.h> 50 #else 51 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h> 52 #endif 53 54 const char * const _masterKeychainName = "FileVaultMaster.keychain"; 55 const char * const _masterKeychainPath = "./FileVaultMaster"; 56 57 /* 58 * Parameters used to create key pairs and certificates in 59 * SR_CertificateAndKeyCreate(). 60 */ 61 #define SR_KEY_ALGORITHM CSSM_ALGID_RSA 62 #define SR_KEY_SIZE_IN_BITS 1024 63 64 #define SR2_KEY_SIZE_IN_BITS 2048 // Recommended size for FileVault2 (FDE) 65 66 /* 67 * The CSSM_ALGORITHMS and OID values defining the signature 68 * algorithm in the generated certificate. 69 */ 70 #define SR_CERT_SIGNATURE_ALGORITHM CSSM_ALGID_SHA256WithRSA 71 #define SR_CERT_SIGNATURE_ALG_OID CSSMOID_SHA256WithRSA 72 73 OSStatus makeMasterPassword(const char *fvmkcName, const char *masterPasswordPassword, uint32 keySizeInBits, SecKeychainRef *keychainRef); 74 75 OSStatus createPair(CFStringRef hostName,CFStringRef userName,SecKeychainRef keychainRef, uint32 keySizeInBits, CFDataRef *cert); 76 OSStatus generateKeyPair(CSSM_CSP_HANDLE cspHand, CSSM_DL_DB_HANDLE dlDbHand, CSSM_ALGORITHMS keyAlg, 77 uint32 keySizeInBits, const char *keyLabel, CSSM_KEY_PTR *pubKeyPtr, CSSM_KEY_PTR *privKeyPtr); 78 OSStatus createRootCert(CSSM_TP_HANDLE tpHand, CSSM_CL_HANDLE clHand, CSSM_CSP_HANDLE cspHand, 79 CSSM_KEY_PTR subjPubKey, CSSM_KEY_PTR signerPrivKey, const char *hostName, const char *userName, 80 CSSM_ALGORITHMS sigAlg, const CSSM_OID *sigOid, CSSM_DATA_PTR certData); 81 82 static char *secCopyCString(CFStringRef theString); 83 84 OSStatus makeMasterPassword(const char *fvmkcName, const char *masterPasswordPassword, uint32 keySizeInBits, SecKeychainRef *keychainRef) 85 { 86 /* 87 OSStatus SecFileVaultMakeMasterPassword(CFStringRef masterPasswordPassword); 88 89 *** In the real code, this will be done directly rather than exec'ing a tool, since there are too many parameters to specify 90 *** this needs to be done as root, since the keychain will be a system keychain 91 /usr/bin/certtool y c k=/Library/Keychains/FileVaultMaster.keychain p=<masterPasswordPassword> 92 /usr/bin/certtool c k=/Library/Keychains/FileVaultMaster.keychain o=/Library/Keychains/FileVaultMaster.cer 93 Two steps: create the keychain, then create the keypair 94 */ 95 96 SecAccessRef initialAccess = NULL; 97 98 if (!masterPasswordPassword) 99 { 100 sec_error("You must supply a non-empty password"); 101 return -2; 102 } 103 104 // We return an error if the keychain already exists 105 OSStatus status = SecKeychainCreate(fvmkcName, (UInt32) strlen(masterPasswordPassword), masterPasswordPassword, false, initialAccess, keychainRef); 106 if (status!=noErr) 107 { 108 if (status==errSecDuplicateKeychain || status==CSSMERR_DL_DATASTORE_ALREADY_EXISTS) { 109 sec_error("The keychain file %s already exists", fvmkcName); 110 } else if (status != errSecSuccess) { 111 sec_error("Could not create keychain file %s: %s", fvmkcName, sec_errstr(status)); 112 } 113 return status; 114 } 115 116 // Create the key pair 117 char host[PATH_MAX]; 118 int rx = gethostname(host, sizeof(host)); 119 if (rx) 120 strcpy(host, "localhost"); 121 122 CFStringRef hostName = CFSTR("FileVault Recovery Key"); // This is what shows up in Keychain Access display 123 CFStringRef userName = CFStringCreateWithCString(kCFAllocatorDefault, host, kCFStringEncodingUTF8); 124 CFDataRef certData = NULL; 125 printf("Generating a %d bit key pair; this may take several minutes\n", keySizeInBits); 126 status = createPair(hostName,userName,*keychainRef,keySizeInBits, &certData); 127 CFReleaseNull(userName); 128 if (status) 129 sec_error("Error in createPair: %s", sec_errstr(status)); 130 if (certData) 131 CFRelease(certData); 132 133 return status; 134 } 135 136 OSStatus createPair(CFStringRef hostName,CFStringRef userName,SecKeychainRef keychainRef, uint32 keySizeInBits, CFDataRef *cert) 137 { 138 SecCertificateRef certRef = NULL; 139 CSSM_DL_DB_HANDLE dlDbHand = {0, 0}; 140 CSSM_CSP_HANDLE cspHand = 0; 141 CSSM_TP_HANDLE tpHand = 0; 142 CSSM_CL_HANDLE clHand = 0; 143 CSSM_KEY_PTR pubKey = NULL; 144 CSSM_KEY_PTR privKey = NULL; 145 CSSM_DATA certData = {0, NULL}; 146 char *hostStr = NULL; 147 char *userStr = NULL; 148 OSStatus ortn; 149 CSSM_OID algOid = SR_CERT_SIGNATURE_ALG_OID; 150 151 hostStr = secCopyCString(hostName); 152 userStr = secCopyCString(userName); 153 if (!hostStr || !userStr) // could not convert to UTF-8 154 { 155 ortn = paramErr; 156 goto xit; 157 } 158 159 // open keychain, connect to all the CDSA modules we'll need 160 161 ortn = SecKeychainGetCSPHandle(keychainRef, &cspHand); 162 if (ortn) 163 goto xit; 164 165 ortn = SecKeychainGetDLDBHandle(keychainRef, &dlDbHand); 166 if (ortn) 167 goto xit; 168 169 tpHand = srTpStartup(); 170 if (tpHand == 0) 171 { 172 ortn = ioErr; 173 goto xit; 174 } 175 176 clHand = srClStartup(); 177 if (clHand == 0) 178 { 179 ortn = ioErr; 180 goto xit; 181 } 182 183 // generate key pair, private key stored in keychain 184 ortn = generateKeyPair(cspHand, dlDbHand, SR_KEY_ALGORITHM, keySizeInBits, 185 "FileVault Master Password Key", &pubKey, &privKey); 186 if (ortn) 187 goto xit; 188 189 // generate the cert 190 ortn = createRootCert(tpHand,clHand,cspHand,pubKey,privKey,hostStr,userStr, 191 SR_CERT_SIGNATURE_ALGORITHM,&algOid,&certData); 192 if (ortn) 193 goto xit; 194 195 // store the cert in the same DL/DB as the key pair [see SecCertificateCreateFromData] 196 ortn = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &certRef); 197 if (ortn) 198 goto xit; 199 200 ortn = SecCertificateAddToKeychain(certRef, keychainRef); 201 if (ortn) 202 goto xit; 203 204 // return the cert to caller 205 *cert = CFDataCreate(NULL, certData.Data, certData.Length); 206 207 // cleanup 208 xit: 209 if (certRef) { 210 CFRelease(certRef); 211 } 212 if (certData.Data) { 213 free(certData.Data); 214 } 215 if (hostStr) { 216 free(hostStr); 217 } 218 if (userStr) { 219 free(userStr); 220 } 221 if (tpHand) { 222 CSSM_ModuleDetach(tpHand); 223 } 224 if (clHand) { 225 CSSM_ModuleDetach(clHand); 226 } 227 if (pubKey) 228 { 229 CSSM_FreeKey(cspHand, 230 NULL, // access cred 231 pubKey, 232 CSSM_FALSE); // delete 233 APP_FREE(pubKey); 234 } 235 if (privKey) 236 { 237 CSSM_FreeKey(cspHand, 238 NULL, // access cred 239 privKey, 240 CSSM_FALSE); // delete 241 APP_FREE(privKey); 242 } 243 244 return ortn; 245 } 246 247 /* 248 * Given a CFStringRef, this function allocates and returns a pointer 249 * to a null-terminated 'C' string copy. If conversion of the string 250 * to UTF8 fails for some reason, the function will return NULL. 251 * 252 * The caller must free this pointer 253 */ 254 255 char *secCopyCString(CFStringRef theString) 256 { 257 CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(theString), kCFStringEncodingUTF8) + 1; 258 char* buffer = (char*) malloc(maxLength); 259 Boolean converted = CFStringGetCString(theString, buffer, maxLength, kCFStringEncodingUTF8); 260 if (!converted) { 261 free(buffer); 262 buffer = NULL; 263 } 264 return buffer; 265 } 266 267 268 #pragma mark -------------------- SecFileVaultCert private implementation -------------------- 269 270 OSStatus createRootCert( 271 CSSM_TP_HANDLE tpHand, 272 CSSM_CL_HANDLE clHand, 273 CSSM_CSP_HANDLE cspHand, 274 CSSM_KEY_PTR subjPubKey, 275 CSSM_KEY_PTR signerPrivKey, 276 const char *hostName, // CSSMOID_CommonName 277 const char *userName, // CSSMOID_Description 278 CSSM_ALGORITHMS sigAlg, 279 const CSSM_OID *sigOid, 280 CSSM_DATA_PTR certData) // mallocd and RETURNED 281 { 282 CE_DataAndType exts[2]; 283 CE_DataAndType *extp = exts; 284 unsigned numExts; 285 CSSM_DATA refId; // mallocd by 286 // CSSM_TP_SubmitCredRequest 287 CSSM_APPLE_TP_CERT_REQUEST certReq; 288 CSSM_TP_REQUEST_SET reqSet; 289 sint32 estTime; 290 CSSM_BOOL confirmRequired; 291 CSSM_TP_RESULT_SET_PTR resultSet=NULL; 292 CSSM_ENCODED_CERT *encCert=NULL; 293 CSSM_APPLE_TP_NAME_OID subjectNames[2]; 294 CSSM_TP_CALLERAUTH_CONTEXT CallerAuthContext; 295 CSSM_FIELD policyId; 296 297 numExts = 0; 298 299 certReq.challengeString = NULL; 300 301 /* KeyUsage extension */ 302 extp->type = DT_KeyUsage; 303 extp->critical = CSSM_FALSE; 304 extp->extension.keyUsage = CE_KU_DigitalSignature | 305 CE_KU_KeyCertSign | 306 CE_KU_KeyEncipherment | 307 CE_KU_DataEncipherment; 308 extp++; 309 numExts++; 310 311 /* BasicConstraints */ 312 extp->type = DT_BasicConstraints; 313 extp->critical = CSSM_TRUE; 314 extp->extension.basicConstraints.cA = CSSM_FALSE; 315 extp->extension.basicConstraints.pathLenConstraintPresent = CSSM_FALSE; 316 extp++; 317 numExts++; 318 319 /* name array */ 320 subjectNames[0].string = hostName; 321 subjectNames[0].oid = &CSSMOID_CommonName; 322 subjectNames[1].string = userName; 323 subjectNames[1].oid = &CSSMOID_Description; 324 325 /* certReq */ 326 certReq.cspHand = cspHand; 327 certReq.clHand = clHand; 328 certReq.serialNumber = arc4random(); 329 certReq.numSubjectNames = 2; 330 certReq.subjectNames = subjectNames; 331 332 certReq.numIssuerNames = 0; // root for now 333 certReq.issuerNames = NULL; 334 certReq.issuerNameX509 = NULL; 335 certReq.certPublicKey = subjPubKey; 336 certReq.issuerPrivateKey = signerPrivKey; 337 certReq.signatureAlg = sigAlg; 338 certReq.signatureOid = *sigOid; 339 certReq.notBefore = 0; 340 certReq.notAfter = 60 * 60 * 24 * 365; // seconds from now, one year 341 certReq.numExtensions = numExts; 342 certReq.extensions = exts; 343 344 reqSet.NumberOfRequests = 1; 345 reqSet.Requests = &certReq; 346 347 /* a CSSM_TP_CALLERAUTH_CONTEXT to specify an OID */ 348 memset(&CallerAuthContext, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT)); 349 memset(&policyId, 0, sizeof(CSSM_FIELD)); 350 policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN; 351 352 CallerAuthContext.Policy.NumberOfPolicyIds = 1; 353 CallerAuthContext.Policy.PolicyIds = &policyId; 354 355 CSSM_RETURN crtn = CSSM_TP_SubmitCredRequest(tpHand, 356 NULL, // PreferredAuthority 357 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, 358 &reqSet, 359 &CallerAuthContext, 360 &estTime, 361 &refId); 362 363 if(crtn) { 364 sec_error("CSSM_TP_SubmitCredRequest: %s", sec_errstr(crtn)); 365 goto xit; 366 } 367 crtn = CSSM_TP_RetrieveCredResult(tpHand, 368 &refId, 369 NULL, // CallerAuthCredentials 370 &estTime, 371 &confirmRequired, 372 &resultSet); 373 if(crtn) { 374 sec_error("CSSM_TP_RetrieveCredResult: %s", sec_errstr(crtn)); 375 goto xit; 376 } 377 if(resultSet == NULL) { 378 sec_error("CSSM_TP_RetrieveCredResult: returned NULL result set"); 379 crtn = ioErr; 380 goto xit; 381 } 382 encCert = (CSSM_ENCODED_CERT *)resultSet->Results; 383 certData->Length = encCert->CertBlob.Length; 384 certData->Data = malloc(encCert->CertBlob.Length); 385 if (certData->Data) 386 memcpy(certData->Data, encCert->CertBlob.Data, encCert->CertBlob.Length); 387 crtn = noErr; 388 389 xit: 390 /* free resources allocated by TP */ 391 APP_FREE(refId.Data); 392 if (encCert) 393 { 394 if (encCert->CertBlob.Data) 395 { 396 APP_FREE(encCert->CertBlob.Data); 397 } 398 APP_FREE(encCert); 399 } 400 APP_FREE(resultSet); 401 return crtn; 402 } 403 404 /* Convert a reference key to a raw key. */ 405 static CSSM_RETURN refKeyToRaw( 406 CSSM_CSP_HANDLE cspHand, 407 const CSSM_KEY *refKey, 408 CSSM_KEY_PTR rawKey) // RETURNED 409 { 410 CSSM_CC_HANDLE ccHand; 411 CSSM_RETURN crtn; 412 CSSM_ACCESS_CREDENTIALS creds; 413 414 memset(rawKey, 0, sizeof(CSSM_KEY)); 415 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); 416 crtn = CSSM_CSP_CreateSymmetricContext(cspHand, 417 CSSM_ALGID_NONE, 418 CSSM_ALGMODE_NONE, 419 &creds, // passPhrase 420 NULL, // wrapping key 421 NULL, // init vector 422 CSSM_PADDING_NONE, // Padding 423 0, // Params 424 &ccHand); 425 if(crtn) { 426 sec_error("CSSM_CSP_CreateSymmetricContext: refKeyToRaw context err: %s", sec_errstr(crtn)); 427 return crtn; 428 } 429 430 crtn = CSSM_WrapKey(ccHand, 431 &creds, 432 refKey, 433 NULL, // DescriptiveData 434 rawKey); 435 if(crtn != CSSM_OK) { 436 sec_error("CSSM_WrapKey: refKeyToRaw wrap err: %s", sec_errstr(crtn)); 437 return crtn; 438 } 439 CSSM_DeleteContext(ccHand); 440 return CSSM_OK; 441 } 442 443 /* 444 * Find private key by label, modify its Label attr to be the 445 * hash of the associated public key. 446 */ 447 static CSSM_RETURN setPubKeyHash( 448 CSSM_CSP_HANDLE cspHand, 449 CSSM_DL_DB_HANDLE dlDbHand, 450 const CSSM_KEY *pubOrPrivKey, // to get hash; raw or ref/CSPDL 451 const char *keyLabel) // look up by this 452 { 453 CSSM_QUERY query; 454 CSSM_SELECTION_PREDICATE predicate; 455 CSSM_DB_UNIQUE_RECORD_PTR record = NULL; 456 CSSM_RETURN crtn; 457 CSSM_DATA labelData; 458 CSSM_HANDLE resultHand; 459 460 labelData.Data = (uint8 *)keyLabel; 461 labelData.Length = strlen(keyLabel) + 1; // incl. NULL 462 query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; 463 query.Conjunctive = CSSM_DB_NONE; 464 query.NumSelectionPredicates = 1; 465 predicate.DbOperator = CSSM_DB_EQUAL; 466 467 predicate.Attribute.Info.AttributeNameFormat = 468 CSSM_DB_ATTRIBUTE_NAME_AS_STRING; 469 predicate.Attribute.Info.Label.AttributeName = "Label"; 470 predicate.Attribute.Info.AttributeFormat = 471 CSSM_DB_ATTRIBUTE_FORMAT_BLOB; 472 predicate.Attribute.Value = &labelData; 473 query.SelectionPredicate = &predicate; 474 475 query.QueryLimits.TimeLimit = 0; 476 query.QueryLimits.SizeLimit = 1; 477 query.QueryFlags = 0; 478 479 /* build Record attribute with one attr */ 480 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; 481 CSSM_DB_ATTRIBUTE_DATA attr; 482 attr.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; 483 attr.Info.Label.AttributeName = "Label"; 484 attr.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; 485 486 recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; 487 recordAttrs.NumberOfAttributes = 1; 488 recordAttrs.AttributeData = &attr; 489 490 crtn = CSSM_DL_DataGetFirst(dlDbHand, 491 &query, 492 &resultHand, 493 &recordAttrs, 494 NULL, // hopefully optional ...theData, 495 &record); 496 /* abort only on success */ 497 if(crtn != CSSM_OK) { 498 sec_error("CSSM_DL_DataGetFirst: setPubKeyHash: can't find private key: %s", sec_errstr(crtn)); 499 return crtn; 500 } 501 502 /* 503 * If specified key is a ref key, do NULL unwrap for use with raw CSP. 504 * If the CSPDL and SecurityServer support the key digest passthrough 505 * this is unnecessary. 506 */ 507 CSSM_KEY rawKeyToDigest; 508 if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) { 509 crtn = refKeyToRaw(cspHand, pubOrPrivKey, &rawKeyToDigest); 510 if(crtn) { 511 sec_error("setPubKeyHash: Error converting public key to raw format: %s", sec_errstr(crtn)); 512 return crtn; 513 } 514 } 515 else { 516 /* use as is */ 517 rawKeyToDigest = *pubOrPrivKey; 518 } 519 520 /* connect to raw CSP */ 521 CSSM_CSP_HANDLE rawCspHand = srCspStartup(CSSM_TRUE); 522 if(rawCspHand == 0) { 523 printf("***Error connecting to raw CSP; aborting.\n"); 524 return -1; 525 } 526 527 /* calculate hash of pub key from private or public part */ 528 CSSM_DATA_PTR keyDigest = NULL; 529 CSSM_CC_HANDLE ccHand; 530 crtn = CSSM_CSP_CreatePassThroughContext(rawCspHand, 531 &rawKeyToDigest, 532 &ccHand); 533 if(ccHand == 0) { 534 sec_error("CSSM_CSP_CreatePassThroughContext: Error calculating public key hash. Aborting: %s", sec_errstr(crtn)); 535 return -1; 536 } 537 crtn = CSSM_CSP_PassThrough(ccHand, 538 CSSM_APPLECSP_KEYDIGEST, 539 NULL, 540 (void **)&keyDigest); 541 if(crtn) { 542 sec_error("CSSM_CSP_PassThrough(PUBKEYHASH): Error calculating public key hash. Aborting: %s", sec_errstr(crtn)); 543 return crtn; 544 } 545 if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) { 546 /* created in refKeyToRaw().... */ 547 CSSM_FreeKey(cspHand, NULL, &rawKeyToDigest, CSSM_FALSE); 548 } 549 CSSM_DeleteContext(ccHand); 550 CSSM_ModuleDetach(rawCspHand); 551 552 /* 553 * Replace Label attr data with hash. 554 * NOTE: the module which allocated this attribute data - a DL - 555 * was loaded and attached by the Sec layer, not by us. Thus 556 * we can't use the memory allocator functions *we* used when 557 * attaching to the CSPDL - we have to use the ones 558 * which the Sec layer registered with the DL. 559 */ 560 CSSM_API_MEMORY_FUNCS memFuncs; 561 crtn = CSSM_GetAPIMemoryFunctions(dlDbHand.DLHandle, &memFuncs); 562 if(crtn) { 563 sec_error("CSSM_GetAPIMemoryFunctions(DLHandle): Error: %s", sec_errstr(crtn)); 564 /* oh well, leak and continue */ 565 } 566 else { 567 memFuncs.free_func(attr.Value->Data, memFuncs.AllocRef); 568 memFuncs.free_func(attr.Value, memFuncs.AllocRef); 569 } 570 attr.Value = keyDigest; 571 572 /* modify key attributes */ 573 crtn = CSSM_DL_DataModify(dlDbHand, 574 CSSM_DL_DB_RECORD_PRIVATE_KEY, 575 record, 576 &recordAttrs, 577 NULL, // DataToBeModified 578 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); 579 if(crtn) { 580 sec_error("CSSM_DL_DataModify(PUBKEYHASH): Error setting public key hash. Aborting: %s", sec_errstr(crtn)); 581 return crtn; 582 } 583 crtn = CSSM_DL_DataAbortQuery(dlDbHand, resultHand); 584 if(crtn) { 585 sec_error("CSSM_DL_DataAbortQuery: Error while stopping query: %s", sec_errstr(crtn)); 586 /* let's keep going in this case */ 587 } 588 crtn = CSSM_DL_FreeUniqueRecord(dlDbHand, record); 589 if(crtn) { 590 sec_error("CSSM_DL_FreeUniqueRecord: Error while freeing record: %s", sec_errstr(crtn)); 591 /* let's keep going in this case */ 592 crtn = CSSM_OK; 593 } 594 595 /* free resources */ 596 if (keyDigest) 597 { 598 srAppFree(keyDigest->Data, NULL); 599 srAppFree(keyDigest, NULL); 600 } 601 return CSSM_OK; 602 } 603 604 /* 605 * Generate a key pair using the CSPDL. 606 */ 607 OSStatus generateKeyPair( 608 CSSM_CSP_HANDLE cspHand, 609 CSSM_DL_DB_HANDLE dlDbHand, 610 CSSM_ALGORITHMS keyAlg, // e.g., CSSM_ALGID_RSA 611 uint32 keySizeInBits, 612 const char *keyLabel, // C string 613 CSSM_KEY_PTR *pubKeyPtr, // mallocd, created, RETURNED 614 CSSM_KEY_PTR *privKeyPtr) // mallocd, created, RETURNED 615 { 616 CSSM_KEY_PTR pubKey = (CSSM_KEY_PTR)(APP_MALLOC(sizeof(CSSM_KEY))); 617 CSSM_KEY_PTR privKey = (CSSM_KEY_PTR)(APP_MALLOC(sizeof(CSSM_KEY))); 618 if((pubKey == NULL) || (privKey == NULL)) { 619 return memFullErr; 620 } 621 622 CSSM_RETURN crtn; 623 CSSM_KEYUSE pubKeyUse; 624 CSSM_KEYUSE privKeyUse; 625 626 pubKeyUse = CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_ENCRYPT | 627 CSSM_KEYUSE_WRAP; 628 privKeyUse = CSSM_KEYUSE_SIGN | CSSM_KEYUSE_DECRYPT | 629 CSSM_KEYUSE_UNWRAP; 630 631 crtn = srCspGenKeyPair(cspHand, 632 &dlDbHand, 633 keyAlg, 634 keyLabel, 635 (int) strlen(keyLabel) + 1, 636 keySizeInBits, 637 pubKey, 638 pubKeyUse, 639 CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_RETURN_REF, 640 privKey, 641 privKeyUse, 642 CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_RETURN_REF | 643 CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE); 644 645 if(crtn) { 646 APP_FREE(pubKey); 647 APP_FREE(privKey); 648 return paramErr; 649 } 650 651 /* bind private key to cert by public key hash */ 652 crtn = setPubKeyHash(cspHand, 653 dlDbHand, 654 pubKey, 655 keyLabel); 656 if(crtn) { 657 sec_error("setPubKeyHash: Error setting public key hash. Continuing at peril: %s", sec_errstr(crtn)); 658 } 659 660 *pubKeyPtr = pubKey; 661 *privKeyPtr = privKey; 662 return noErr; 663 } 664 665 //========================================================================== 666 667 int 668 keychain_createMFV(int argc, char * const *argv) 669 { 670 int zero_password = 0; 671 char *password = NULL; 672 const char *keychainName = NULL; 673 int result = 0, ch = 0; 674 Boolean do_prompt = FALSE; 675 SecKeychainRef keychainRef = NULL; 676 uint32 keySizeInBits = SR2_KEY_SIZE_IN_BITS; // default 677 678 /* AG: getopts optstring name [args] 679 AG: while loop calling getopt is used to extract password from cl from user 680 password is the only option to keychain_create 681 optstring is a string containing the legitimate option 682 characters. If such a character is followed by a colon, 683 the option requires an argument, so getopt places a 684 pointer to the following text in the same argv-element, or 685 the text of the following argv-element, in optarg. 686 */ 687 while ((ch = getopt(argc, argv, "hp:s:P")) != -1) 688 { 689 switch (ch) 690 { 691 case 'p': 692 password = optarg; 693 break; 694 case 'P': 695 do_prompt = TRUE; 696 break; 697 case 's': 698 // Specify the keysize in bits (default 1024) 699 keySizeInBits = atoi(optarg); 700 if (!(keySizeInBits == SR_KEY_SIZE_IN_BITS || keySizeInBits == SR2_KEY_SIZE_IN_BITS || keySizeInBits == 4096)) 701 return SHOW_USAGE_MESSAGE; 702 break; 703 case '?': 704 default: 705 return SHOW_USAGE_MESSAGE; 706 } 707 } 708 /* 709 AG: The external variable optind is the index of the next 710 array element of argv[] to be processed; it communicates 711 from one call of getopt() to the next which element to 712 process. 713 The variable optind is the index of the next element of the argv[] vector to be processed. It shall be initialized to 1 by the system, and getopt() shall update it when it finishes with each element of argv[]. When an element of argv[] contains multiple option characters, it is unspecified how getopt() determines which options have already been processed. 714 715 */ 716 argc -= optind; 717 argv += optind; 718 719 if (argc > 1) 720 return SHOW_USAGE_MESSAGE; 721 722 keychainName = (argc == 1)?*argv:_masterKeychainName; 723 if (!keychainName || *keychainName == '\0') { 724 return -1; 725 } 726 727 if (!password && !do_prompt) 728 { 729 int compare = 1; 730 int tries; 731 732 for (tries = 3; tries-- > 0;) 733 { 734 char *firstpass; 735 736 password = getpass("password for new keychain: "); 737 if (!password) 738 { 739 result = -1; 740 goto loser; 741 } 742 743 firstpass = malloc(strlen(password) + 1); 744 strcpy(firstpass, password); 745 password = getpass("retype password for new keychain: "); 746 compare = password ? strcmp(password, firstpass) : 1; 747 memset(firstpass, 0, strlen(firstpass)); 748 free(firstpass); 749 if (!password) 750 { 751 result = -1; 752 goto loser; 753 } 754 755 if (compare) 756 { 757 fprintf(stderr, "passwords don't match\n"); 758 memset(password, 0, strlen(password)); 759 } 760 else 761 { 762 zero_password = 1; 763 break; 764 } 765 } 766 767 if (compare) 768 { 769 result = 1; 770 goto loser; 771 } 772 } 773 774 do 775 { 776 // result = do_create(keychain, password, do_prompt); 777 result = makeMasterPassword(keychainName, password, keySizeInBits, &keychainRef); 778 if (keychainRef) 779 CFRelease(keychainRef); 780 if (zero_password) 781 memset(password, 0, strlen(password)); 782 if (result) 783 goto loser; 784 785 argc--; 786 argv++; 787 keychainName = *argv; 788 } while (argc > 0); 789 790 loser: 791 792 return result; 793 }