]> git.saurik.com Git - apple/security.git/blob - SecurityTool/createFVMaster.c
Security-57740.31.2.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.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
45 #include "srCdsaUtils.h"
46
47 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
48
49 const char * const _masterKeychainName = "FileVaultMaster.keychain";
50 const 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_SHA256WithRSA
66 #define SR_CERT_SIGNATURE_ALG_OID CSSMOID_SHA256WithRSA
67
68 OSStatus makeMasterPassword(const char *fvmkcName, const char *masterPasswordPassword, uint32 keySizeInBits, SecKeychainRef *keychainRef);
69
70 OSStatus createPair(CFStringRef hostName,CFStringRef userName,SecKeychainRef keychainRef, uint32 keySizeInBits, CFDataRef *cert);
71 OSStatus 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);
73 OSStatus 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);
76 void randUint32(uint32 *u);
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 if (status)
121 sec_error("Error in createPair: %s", sec_errstr(status));
122 if (certData)
123 CFRelease(certData);
124
125 return status;
126 }
127
128 OSStatus 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
200 xit:
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
241 char *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
256 OSStatus 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=NULL;
278 CSSM_ENCODED_CERT *encCert=NULL;
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
375 xit:
376 /* free resources allocated by TP */
377 APP_FREE(refId.Data);
378 if (encCert)
379 {
380 if (encCert->CertBlob.Data)
381 {
382 APP_FREE(encCert->CertBlob.Data);
383 }
384 APP_FREE(encCert);
385 }
386 APP_FREE(resultSet);
387 return crtn;
388 }
389
390 /* Convert a reference key to a raw key. */
391 static CSSM_RETURN refKeyToRaw(
392 CSSM_CSP_HANDLE cspHand,
393 const CSSM_KEY *refKey,
394 CSSM_KEY_PTR rawKey) // RETURNED
395 {
396 CSSM_CC_HANDLE ccHand;
397 CSSM_RETURN crtn;
398 CSSM_ACCESS_CREDENTIALS creds;
399
400 memset(rawKey, 0, sizeof(CSSM_KEY));
401 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
402 crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
403 CSSM_ALGID_NONE,
404 CSSM_ALGMODE_NONE,
405 &creds, // passPhrase
406 NULL, // wrapping key
407 NULL, // init vector
408 CSSM_PADDING_NONE, // Padding
409 0, // Params
410 &ccHand);
411 if(crtn) {
412 sec_error("CSSM_CSP_CreateSymmetricContext: refKeyToRaw context err: %s", sec_errstr(crtn));
413 return crtn;
414 }
415
416 crtn = CSSM_WrapKey(ccHand,
417 &creds,
418 refKey,
419 NULL, // DescriptiveData
420 rawKey);
421 if(crtn != CSSM_OK) {
422 sec_error("CSSM_WrapKey: refKeyToRaw wrap err: %s", sec_errstr(crtn));
423 return crtn;
424 }
425 CSSM_DeleteContext(ccHand);
426 return CSSM_OK;
427 }
428
429 /*
430 * Find private key by label, modify its Label attr to be the
431 * hash of the associated public key.
432 */
433 static CSSM_RETURN setPubKeyHash(
434 CSSM_CSP_HANDLE cspHand,
435 CSSM_DL_DB_HANDLE dlDbHand,
436 const CSSM_KEY *pubOrPrivKey, // to get hash; raw or ref/CSPDL
437 const char *keyLabel) // look up by this
438 {
439 CSSM_QUERY query;
440 CSSM_SELECTION_PREDICATE predicate;
441 CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
442 CSSM_RETURN crtn;
443 CSSM_DATA labelData;
444 CSSM_HANDLE resultHand;
445
446 labelData.Data = (uint8 *)keyLabel;
447 labelData.Length = strlen(keyLabel) + 1; // incl. NULL
448 query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
449 query.Conjunctive = CSSM_DB_NONE;
450 query.NumSelectionPredicates = 1;
451 predicate.DbOperator = CSSM_DB_EQUAL;
452
453 predicate.Attribute.Info.AttributeNameFormat =
454 CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
455 predicate.Attribute.Info.Label.AttributeName = "Label";
456 predicate.Attribute.Info.AttributeFormat =
457 CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
458 predicate.Attribute.Value = &labelData;
459 query.SelectionPredicate = &predicate;
460
461 query.QueryLimits.TimeLimit = 0;
462 query.QueryLimits.SizeLimit = 1;
463 query.QueryFlags = 0;
464
465 /* build Record attribute with one attr */
466 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
467 CSSM_DB_ATTRIBUTE_DATA attr;
468 attr.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
469 attr.Info.Label.AttributeName = "Label";
470 attr.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
471
472 recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
473 recordAttrs.NumberOfAttributes = 1;
474 recordAttrs.AttributeData = &attr;
475
476 crtn = CSSM_DL_DataGetFirst(dlDbHand,
477 &query,
478 &resultHand,
479 &recordAttrs,
480 NULL, // hopefully optional ...theData,
481 &record);
482 /* abort only on success */
483 if(crtn != CSSM_OK) {
484 sec_error("CSSM_DL_DataGetFirst: setPubKeyHash: can't find private key: %s", sec_errstr(crtn));
485 return crtn;
486 }
487
488 /*
489 * If specified key is a ref key, do NULL unwrap for use with raw CSP.
490 * If the CSPDL and SecurityServer support the key digest passthrough
491 * this is unnecessary.
492 */
493 CSSM_KEY rawKeyToDigest;
494 if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) {
495 crtn = refKeyToRaw(cspHand, pubOrPrivKey, &rawKeyToDigest);
496 if(crtn) {
497 sec_error("setPubKeyHash: Error converting public key to raw format: %s", sec_errstr(crtn));
498 return crtn;
499 }
500 }
501 else {
502 /* use as is */
503 rawKeyToDigest = *pubOrPrivKey;
504 }
505
506 /* connect to raw CSP */
507 CSSM_CSP_HANDLE rawCspHand = srCspStartup(CSSM_TRUE);
508 if(rawCspHand == 0) {
509 printf("***Error connecting to raw CSP; aborting.\n");
510 return -1;
511 }
512
513 /* calculate hash of pub key from private or public part */
514 CSSM_DATA_PTR keyDigest = NULL;
515 CSSM_CC_HANDLE ccHand;
516 crtn = CSSM_CSP_CreatePassThroughContext(rawCspHand,
517 &rawKeyToDigest,
518 &ccHand);
519 if(ccHand == 0) {
520 sec_error("CSSM_CSP_CreatePassThroughContext: Error calculating public key hash. Aborting: %s", sec_errstr(crtn));
521 return -1;
522 }
523 crtn = CSSM_CSP_PassThrough(ccHand,
524 CSSM_APPLECSP_KEYDIGEST,
525 NULL,
526 (void **)&keyDigest);
527 if(crtn) {
528 sec_error("CSSM_CSP_PassThrough(PUBKEYHASH): Error calculating public key hash. Aborting: %s", sec_errstr(crtn));
529 return crtn;
530 }
531 if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) {
532 /* created in refKeyToRaw().... */
533 CSSM_FreeKey(cspHand, NULL, &rawKeyToDigest, CSSM_FALSE);
534 }
535 CSSM_DeleteContext(ccHand);
536 CSSM_ModuleDetach(rawCspHand);
537
538 /*
539 * Replace Label attr data with hash.
540 * NOTE: the module which allocated this attribute data - a DL -
541 * was loaded and attached by the Sec layer, not by us. Thus
542 * we can't use the memory allocator functions *we* used when
543 * attaching to the CSPDL - we have to use the ones
544 * which the Sec layer registered with the DL.
545 */
546 CSSM_API_MEMORY_FUNCS memFuncs;
547 crtn = CSSM_GetAPIMemoryFunctions(dlDbHand.DLHandle, &memFuncs);
548 if(crtn) {
549 sec_error("CSSM_GetAPIMemoryFunctions(DLHandle): Error: %s", sec_errstr(crtn));
550 /* oh well, leak and continue */
551 }
552 else {
553 memFuncs.free_func(attr.Value->Data, memFuncs.AllocRef);
554 memFuncs.free_func(attr.Value, memFuncs.AllocRef);
555 }
556 attr.Value = keyDigest;
557
558 /* modify key attributes */
559 crtn = CSSM_DL_DataModify(dlDbHand,
560 CSSM_DL_DB_RECORD_PRIVATE_KEY,
561 record,
562 &recordAttrs,
563 NULL, // DataToBeModified
564 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
565 if(crtn) {
566 sec_error("CSSM_DL_DataModify(PUBKEYHASH): Error setting public key hash. Aborting: %s", sec_errstr(crtn));
567 return crtn;
568 }
569 crtn = CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
570 if(crtn) {
571 sec_error("CSSM_DL_DataAbortQuery: Error while stopping query: %s", sec_errstr(crtn));
572 /* let's keep going in this case */
573 }
574 crtn = CSSM_DL_FreeUniqueRecord(dlDbHand, record);
575 if(crtn) {
576 sec_error("CSSM_DL_FreeUniqueRecord: Error while freeing record: %s", sec_errstr(crtn));
577 /* let's keep going in this case */
578 crtn = CSSM_OK;
579 }
580
581 /* free resources */
582 if (keyDigest)
583 {
584 srAppFree(keyDigest->Data, NULL);
585 srAppFree(keyDigest, NULL);
586 }
587 return CSSM_OK;
588 }
589
590 /*
591 * Generate a key pair using the CSPDL.
592 */
593 OSStatus generateKeyPair(
594 CSSM_CSP_HANDLE cspHand,
595 CSSM_DL_DB_HANDLE dlDbHand,
596 CSSM_ALGORITHMS keyAlg, // e.g., CSSM_ALGID_RSA
597 uint32 keySizeInBits,
598 const char *keyLabel, // C string
599 CSSM_KEY_PTR *pubKeyPtr, // mallocd, created, RETURNED
600 CSSM_KEY_PTR *privKeyPtr) // mallocd, created, RETURNED
601 {
602 CSSM_KEY_PTR pubKey = (CSSM_KEY_PTR)(APP_MALLOC(sizeof(CSSM_KEY)));
603 CSSM_KEY_PTR privKey = (CSSM_KEY_PTR)(APP_MALLOC(sizeof(CSSM_KEY)));
604 if((pubKey == NULL) || (privKey == NULL)) {
605 return memFullErr;
606 }
607
608 CSSM_RETURN crtn;
609 CSSM_KEYUSE pubKeyUse;
610 CSSM_KEYUSE privKeyUse;
611
612 pubKeyUse = CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_ENCRYPT |
613 CSSM_KEYUSE_WRAP;
614 privKeyUse = CSSM_KEYUSE_SIGN | CSSM_KEYUSE_DECRYPT |
615 CSSM_KEYUSE_UNWRAP;
616
617 crtn = srCspGenKeyPair(cspHand,
618 &dlDbHand,
619 keyAlg,
620 keyLabel,
621 (int) strlen(keyLabel) + 1,
622 keySizeInBits,
623 pubKey,
624 pubKeyUse,
625 CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_RETURN_REF,
626 privKey,
627 privKeyUse,
628 CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_RETURN_REF |
629 CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE);
630
631 if(crtn) {
632 APP_FREE(pubKey);
633 APP_FREE(privKey);
634 return paramErr;
635 }
636
637 /* bind private key to cert by public key hash */
638 crtn = setPubKeyHash(cspHand,
639 dlDbHand,
640 pubKey,
641 keyLabel);
642 if(crtn) {
643 sec_error("setPubKeyHash: Error setting public key hash. Continuing at peril: %s", sec_errstr(crtn));
644 }
645
646 *pubKeyPtr = pubKey;
647 *privKeyPtr = privKey;
648 return noErr;
649 }
650
651 // Fill a uint32 with random data
652 void randUint32(uint32 *u)
653 {
654 int dev = open("/dev/random", O_RDONLY);
655 if(dev < 0) {
656 return;
657 }
658 read(dev, u, sizeof(*u));
659 close(dev);
660 }
661
662
663 //==========================================================================
664
665 int
666 keychain_createMFV(int argc, char * const *argv)
667 {
668 int zero_password = 0;
669 char *password = NULL;
670 const char *keychainName = NULL;
671 int result = 0, ch = 0;
672 Boolean do_prompt = FALSE;
673 SecKeychainRef keychainRef = NULL;
674 uint32 keySizeInBits = SR2_KEY_SIZE_IN_BITS; // default
675
676 /* AG: getopts optstring name [args]
677 AG: while loop calling getopt is used to extract password from cl from user
678 password is the only option to keychain_create
679 optstring is a string containing the legitimate option
680 characters. If such a character is followed by a colon,
681 the option requires an argument, so getopt places a
682 pointer to the following text in the same argv-element, or
683 the text of the following argv-element, in optarg.
684 */
685 while ((ch = getopt(argc, argv, "hp:s:P")) != -1)
686 {
687 switch (ch)
688 {
689 case 'p':
690 password = optarg;
691 break;
692 case 'P':
693 do_prompt = TRUE;
694 break;
695 case 's':
696 // Specify the keysize in bits (default 1024)
697 keySizeInBits = atoi(optarg);
698 if (!(keySizeInBits == SR_KEY_SIZE_IN_BITS || keySizeInBits == SR2_KEY_SIZE_IN_BITS || keySizeInBits == 4096))
699 return 2;
700 break;
701 case '?':
702 default:
703 return 2; /* @@@ Return 2 triggers usage message. */
704 }
705 }
706 /*
707 AG: The external variable optind is the index of the next
708 array element of argv[] to be processed; it communicates
709 from one call of getopt() to the next which element to
710 process.
711 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.
712
713 */
714 argc -= optind;
715 argv += optind;
716
717 if (argc > 1)
718 return 2; /* @@@ Return 2 triggers usage message. */
719
720 keychainName = (argc == 1)?*argv:_masterKeychainName;
721 if (!keychainName || *keychainName == '\0')
722 return -1;
723
724 if (!password && !do_prompt)
725 {
726 int compare = 1;
727 int tries;
728
729 for (tries = 3; tries-- > 0;)
730 {
731 char *firstpass;
732
733 password = getpass("password for new keychain: ");
734 if (!password)
735 {
736 result = -1;
737 goto loser;
738 }
739
740 firstpass = malloc(strlen(password) + 1);
741 strcpy(firstpass, password);
742 password = getpass("retype password for new keychain: ");
743 compare = password ? strcmp(password, firstpass) : 1;
744 memset(firstpass, 0, strlen(firstpass));
745 free(firstpass);
746 if (!password)
747 {
748 result = -1;
749 goto loser;
750 }
751
752 if (compare)
753 {
754 fprintf(stderr, "passwords don't match\n");
755 memset(password, 0, strlen(password));
756 }
757 else
758 {
759 zero_password = 1;
760 break;
761 }
762 }
763
764 if (compare)
765 {
766 result = 1;
767 goto loser;
768 }
769 }
770
771 do
772 {
773 // result = do_create(keychain, password, do_prompt);
774 result = makeMasterPassword(keychainName, password, keySizeInBits, &keychainRef);
775 if (keychainRef)
776 CFRelease(keychainRef);
777 if (zero_password)
778 memset(password, 0, strlen(password));
779 if (result)
780 goto loser;
781
782 argc--;
783 argv++;
784 keychainName = *argv;
785 } while (argc > 0);
786
787 loser:
788
789 return result;
790 }