2 * Copyright (c) 2011-2012,2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #include "createFVMaster.h"
28 #include "readline_cssm.h"
29 #include "security_tool.h"
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>
44 #include <utilities/SecCFRelease.h>
46 #include "srCdsaUtils.h"
48 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
50 const char * const _masterKeychainName
= "FileVaultMaster.keychain";
51 const char * const _masterKeychainPath
= "./FileVaultMaster";
54 * Parameters used to create key pairs and certificates in
55 * SR_CertificateAndKeyCreate().
57 #define SR_KEY_ALGORITHM CSSM_ALGID_RSA
58 #define SR_KEY_SIZE_IN_BITS 1024
60 #define SR2_KEY_SIZE_IN_BITS 2048 // Recommended size for FileVault2 (FDE)
63 * The CSSM_ALGORITHMS and OID values defining the signature
64 * algorithm in the generated certificate.
66 #define SR_CERT_SIGNATURE_ALGORITHM CSSM_ALGID_SHA256WithRSA
67 #define SR_CERT_SIGNATURE_ALG_OID CSSMOID_SHA256WithRSA
69 OSStatus
makeMasterPassword(const char *fvmkcName
, const char *masterPasswordPassword
, uint32 keySizeInBits
, SecKeychainRef
*keychainRef
);
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
);
78 static char *secCopyCString(CFStringRef theString
);
80 OSStatus
makeMasterPassword(const char *fvmkcName
, const char *masterPasswordPassword
, uint32 keySizeInBits
, SecKeychainRef
*keychainRef
)
83 OSStatus SecFileVaultMakeMasterPassword(CFStringRef masterPasswordPassword);
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
92 SecAccessRef initialAccess
= NULL
;
94 if (!masterPasswordPassword
)
96 sec_error("You must supply a non-empty password");
100 // We return an error if the keychain already exists
101 OSStatus status
= SecKeychainCreate(fvmkcName
, (UInt32
) strlen(masterPasswordPassword
), masterPasswordPassword
, false, initialAccess
, keychainRef
);
104 if (status
==errSecDuplicateKeychain
|| status
==CSSMERR_DL_DATASTORE_ALREADY_EXISTS
)
105 sec_error("The keychain file %s already exists", fvmkcName
);
109 // Create the key pair
111 int rx
= gethostname(host
, sizeof(host
));
113 strcpy(host
, "localhost");
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
);
122 sec_error("Error in createPair: %s", sec_errstr(status
));
129 OSStatus
createPair(CFStringRef hostName
,CFStringRef userName
,SecKeychainRef keychainRef
, uint32 keySizeInBits
, CFDataRef
*cert
)
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
;
142 CSSM_OID algOid
= SR_CERT_SIGNATURE_ALG_OID
;
144 hostStr
= secCopyCString(hostName
);
145 userStr
= secCopyCString(userName
);
146 if (!hostStr
|| !userStr
) // could not convert to UTF-8
152 // open keychain, connect to all the CDSA modules we'll need
154 ortn
= SecKeychainGetCSPHandle(keychainRef
, &cspHand
);
158 ortn
= SecKeychainGetDLDBHandle(keychainRef
, &dlDbHand
);
162 tpHand
= srTpStartup();
169 clHand
= srClStartup();
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
);
183 ortn
= createRootCert(tpHand
,clHand
,cspHand
,pubKey
,privKey
,hostStr
,userStr
,
184 SR_CERT_SIGNATURE_ALGORITHM
,&algOid
,&certData
);
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
);
193 ortn
= SecCertificateAddToKeychain(certRef
, keychainRef
);
197 // return the cert to caller
198 *cert
= CFDataCreate(NULL
, certData
.Data
, certData
.Length
);
211 CSSM_ModuleDetach(tpHand
);
213 CSSM_ModuleDetach(clHand
);
216 CSSM_FreeKey(cspHand
,
219 CSSM_FALSE
); // delete
224 CSSM_FreeKey(cspHand
,
227 CSSM_FALSE
); // delete
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.
239 * The caller must free this pointer
242 char *secCopyCString(CFStringRef theString
)
244 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(theString
), kCFStringEncodingUTF8
) + 1;
245 char* buffer
= (char*) malloc(maxLength
);
246 Boolean converted
= CFStringGetCString(theString
, buffer
, maxLength
, kCFStringEncodingUTF8
);
255 #pragma mark -------------------- SecFileVaultCert private implementation --------------------
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
269 CE_DataAndType exts
[2];
270 CE_DataAndType
*extp
= exts
;
272 CSSM_DATA refId
; // mallocd by
273 // CSSM_TP_SubmitCredRequest
274 CSSM_APPLE_TP_CERT_REQUEST certReq
;
275 CSSM_TP_REQUEST_SET reqSet
;
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
;
286 certReq
.challengeString
= NULL
;
288 /* KeyUsage extension */
289 extp
->type
= DT_KeyUsage
;
290 extp
->critical
= CSSM_FALSE
;
291 extp
->extension
.keyUsage
= CE_KU_DigitalSignature
|
293 CE_KU_KeyEncipherment
|
294 CE_KU_DataEncipherment
;
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
;
307 subjectNames
[0].string
= hostName
;
308 subjectNames
[0].oid
= &CSSMOID_CommonName
;
309 subjectNames
[1].string
= userName
;
310 subjectNames
[1].oid
= &CSSMOID_Description
;
313 certReq
.cspHand
= cspHand
;
314 certReq
.clHand
= clHand
;
315 certReq
.serialNumber
= arc4random();
316 certReq
.numSubjectNames
= 2;
317 certReq
.subjectNames
= subjectNames
;
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
;
331 reqSet
.NumberOfRequests
= 1;
332 reqSet
.Requests
= &certReq
;
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
;
339 CallerAuthContext
.Policy
.NumberOfPolicyIds
= 1;
340 CallerAuthContext
.Policy
.PolicyIds
= &policyId
;
342 CSSM_RETURN crtn
= CSSM_TP_SubmitCredRequest(tpHand
,
343 NULL
, // PreferredAuthority
344 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE
,
351 sec_error("CSSM_TP_SubmitCredRequest: %s", sec_errstr(crtn
));
354 crtn
= CSSM_TP_RetrieveCredResult(tpHand
,
356 NULL
, // CallerAuthCredentials
361 sec_error("CSSM_TP_RetrieveCredResult: %s", sec_errstr(crtn
));
364 if(resultSet
== NULL
) {
365 sec_error("CSSM_TP_RetrieveCredResult: returned NULL result set");
369 encCert
= (CSSM_ENCODED_CERT
*)resultSet
->Results
;
370 certData
->Length
= encCert
->CertBlob
.Length
;
371 certData
->Data
= malloc(encCert
->CertBlob
.Length
);
373 memcpy(certData
->Data
, encCert
->CertBlob
.Data
, encCert
->CertBlob
.Length
);
377 /* free resources allocated by TP */
378 APP_FREE(refId
.Data
);
381 if (encCert
->CertBlob
.Data
)
383 APP_FREE(encCert
->CertBlob
.Data
);
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
397 CSSM_CC_HANDLE ccHand
;
399 CSSM_ACCESS_CREDENTIALS creds
;
401 memset(rawKey
, 0, sizeof(CSSM_KEY
));
402 memset(&creds
, 0, sizeof(CSSM_ACCESS_CREDENTIALS
));
403 crtn
= CSSM_CSP_CreateSymmetricContext(cspHand
,
406 &creds
, // passPhrase
407 NULL
, // wrapping key
409 CSSM_PADDING_NONE
, // Padding
413 sec_error("CSSM_CSP_CreateSymmetricContext: refKeyToRaw context err: %s", sec_errstr(crtn
));
417 crtn
= CSSM_WrapKey(ccHand
,
420 NULL
, // DescriptiveData
422 if(crtn
!= CSSM_OK
) {
423 sec_error("CSSM_WrapKey: refKeyToRaw wrap err: %s", sec_errstr(crtn
));
426 CSSM_DeleteContext(ccHand
);
431 * Find private key by label, modify its Label attr to be the
432 * hash of the associated public key.
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
441 CSSM_SELECTION_PREDICATE predicate
;
442 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
445 CSSM_HANDLE resultHand
;
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
;
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
;
462 query
.QueryLimits
.TimeLimit
= 0;
463 query
.QueryLimits
.SizeLimit
= 1;
464 query
.QueryFlags
= 0;
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
;
473 recordAttrs
.DataRecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
474 recordAttrs
.NumberOfAttributes
= 1;
475 recordAttrs
.AttributeData
= &attr
;
477 crtn
= CSSM_DL_DataGetFirst(dlDbHand
,
481 NULL
, // hopefully optional ...theData,
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
));
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.
494 CSSM_KEY rawKeyToDigest
;
495 if(pubOrPrivKey
->KeyHeader
.BlobType
== CSSM_KEYBLOB_REFERENCE
) {
496 crtn
= refKeyToRaw(cspHand
, pubOrPrivKey
, &rawKeyToDigest
);
498 sec_error("setPubKeyHash: Error converting public key to raw format: %s", sec_errstr(crtn
));
504 rawKeyToDigest
= *pubOrPrivKey
;
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");
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
,
521 sec_error("CSSM_CSP_CreatePassThroughContext: Error calculating public key hash. Aborting: %s", sec_errstr(crtn
));
524 crtn
= CSSM_CSP_PassThrough(ccHand
,
525 CSSM_APPLECSP_KEYDIGEST
,
527 (void **)&keyDigest
);
529 sec_error("CSSM_CSP_PassThrough(PUBKEYHASH): Error calculating public key hash. Aborting: %s", sec_errstr(crtn
));
532 if(pubOrPrivKey
->KeyHeader
.BlobType
== CSSM_KEYBLOB_REFERENCE
) {
533 /* created in refKeyToRaw().... */
534 CSSM_FreeKey(cspHand
, NULL
, &rawKeyToDigest
, CSSM_FALSE
);
536 CSSM_DeleteContext(ccHand
);
537 CSSM_ModuleDetach(rawCspHand
);
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.
547 CSSM_API_MEMORY_FUNCS memFuncs
;
548 crtn
= CSSM_GetAPIMemoryFunctions(dlDbHand
.DLHandle
, &memFuncs
);
550 sec_error("CSSM_GetAPIMemoryFunctions(DLHandle): Error: %s", sec_errstr(crtn
));
551 /* oh well, leak and continue */
554 memFuncs
.free_func(attr
.Value
->Data
, memFuncs
.AllocRef
);
555 memFuncs
.free_func(attr
.Value
, memFuncs
.AllocRef
);
557 attr
.Value
= keyDigest
;
559 /* modify key attributes */
560 crtn
= CSSM_DL_DataModify(dlDbHand
,
561 CSSM_DL_DB_RECORD_PRIVATE_KEY
,
564 NULL
, // DataToBeModified
565 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
567 sec_error("CSSM_DL_DataModify(PUBKEYHASH): Error setting public key hash. Aborting: %s", sec_errstr(crtn
));
570 crtn
= CSSM_DL_DataAbortQuery(dlDbHand
, resultHand
);
572 sec_error("CSSM_DL_DataAbortQuery: Error while stopping query: %s", sec_errstr(crtn
));
573 /* let's keep going in this case */
575 crtn
= CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
577 sec_error("CSSM_DL_FreeUniqueRecord: Error while freeing record: %s", sec_errstr(crtn
));
578 /* let's keep going in this case */
585 srAppFree(keyDigest
->Data
, NULL
);
586 srAppFree(keyDigest
, NULL
);
592 * Generate a key pair using the CSPDL.
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
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
)) {
610 CSSM_KEYUSE pubKeyUse
;
611 CSSM_KEYUSE privKeyUse
;
613 pubKeyUse
= CSSM_KEYUSE_VERIFY
| CSSM_KEYUSE_ENCRYPT
|
615 privKeyUse
= CSSM_KEYUSE_SIGN
| CSSM_KEYUSE_DECRYPT
|
618 crtn
= srCspGenKeyPair(cspHand
,
622 (int) strlen(keyLabel
) + 1,
626 CSSM_KEYATTR_EXTRACTABLE
| CSSM_KEYATTR_RETURN_REF
,
629 CSSM_KEYATTR_SENSITIVE
| CSSM_KEYATTR_RETURN_REF
|
630 CSSM_KEYATTR_PERMANENT
| CSSM_KEYATTR_EXTRACTABLE
);
638 /* bind private key to cert by public key hash */
639 crtn
= setPubKeyHash(cspHand
,
644 sec_error("setPubKeyHash: Error setting public key hash. Continuing at peril: %s", sec_errstr(crtn
));
648 *privKeyPtr
= privKey
;
652 //==========================================================================
655 keychain_createMFV(int argc
, char * const *argv
)
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
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.
674 while ((ch
= getopt(argc
, argv
, "hp:s:P")) != -1)
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))
692 return 2; /* @@@ Return 2 triggers usage message. */
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
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.
707 return 2; /* @@@ Return 2 triggers usage message. */
709 keychainName
= (argc
== 1)?*argv
:_masterKeychainName
;
710 if (!keychainName
|| *keychainName
== '\0')
713 if (!password
&& !do_prompt
)
718 for (tries
= 3; tries
-- > 0;)
722 password
= getpass("password for new keychain: ");
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
));
743 fprintf(stderr
, "passwords don't match\n");
744 memset(password
, 0, strlen(password
));
762 // result = do_create(keychain, password, do_prompt);
763 result
= makeMasterPassword(keychainName
, password
, keySizeInBits
, &keychainRef
);
765 CFRelease(keychainRef
);
767 memset(password
, 0, strlen(password
));
773 keychainName
= *argv
;