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