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