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