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.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
49const char * const _masterKeychainName = "FileVaultMaster.keychain";
50const 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_SHA1WithRSA
66#define SR_CERT_SIGNATURE_ALG_OID	CSSMOID_SHA1WithRSA
67
68OSStatus makeMasterPassword(const char *fvmkcName, const char *masterPasswordPassword, uint32 keySizeInBits, SecKeychainRef *keychainRef);
69
70OSStatus createPair(CFStringRef hostName,CFStringRef userName,SecKeychainRef keychainRef, uint32 keySizeInBits, CFDataRef *cert);
71OSStatus 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);
73OSStatus 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);
76void randUint32(uint32 *u);
77
78static char *secCopyCString(CFStringRef theString);
79
80OSStatus 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, 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
128OSStatus 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
200xit:
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
241char *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
256OSStatus 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
375xit:
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. */
391static 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 */
433CSSM_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 */
593OSStatus 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		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
652void 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
665int
666keychain_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
787loser:
788
789	return result;
790}
791