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