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