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