]> git.saurik.com Git - apple/security.git/blob - Keychain/SecFileVaultCert.cpp
Security-163.tar.gz
[apple/security.git] / Keychain / SecFileVaultCert.cpp
1 /*
2 * Copyright (c) 2003 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please
7 * obtain a copy of the License at http://www.apple.com/publicsource and
8 * read it before using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
12 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
13 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
15 * Please see the License for the specific language governing rights and
16 * limitations under the License.
17 */
18
19 /*
20 * SecFileVaultCert.cpp - Certificate support for FileVault
21 */
22
23 #include "SecFileVaultCert.h"
24 #include "srCdsaUtils.h"
25 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <Security/SecCertificate.h>
29 #include <Security/Certificate.h>
30 #include <Security/SecKeychain.h>
31 #include <Security/KCCursor.h>
32 #include <Security/cfutilities.h>
33 #include <Security/SecRuntime.h>
34 #include <Security/SecCFTypes.h>
35
36 using namespace KeychainCore;
37
38 #pragma mark -------------------- SecFileVaultCert public implementation --------------------
39
40 SecFileVaultCert::SecFileVaultCert()
41 {
42 }
43
44 SecFileVaultCert::~SecFileVaultCert()
45 {
46 }
47
48 OSStatus SecFileVaultCert::createPair(CFStringRef hostName,CFStringRef userName,SecKeychainRef keychainRef, CFDataRef *cert)
49 {
50 SecCertificateRef certRef = NULL;
51 CSSM_DL_DB_HANDLE dlDbHand = {0, 0};
52 CSSM_CSP_HANDLE cspHand = 0;
53 CSSM_TP_HANDLE tpHand = 0;
54 CSSM_CL_HANDLE clHand = 0;
55 CSSM_KEY_PTR pubKey = NULL;
56 CSSM_KEY_PTR privKey = NULL;
57 CSSM_DATA certData = {0, NULL};
58 char *hostStr = NULL;
59 char *userStr = NULL;
60 OSStatus ortn;
61 CSSM_OID algOid = SR_CERT_SIGNATURE_ALG_OID;
62
63 KeychainCore::Keychain keychain = KeychainCore::Keychain::optional(keychainRef);
64
65 hostStr = srCfStrToCString(hostName);
66 userStr = srCfStrToCString(userName);
67 if (!hostStr || !userStr) // probably not ASCII capable
68 MacOSError::throwMe(paramErr);
69
70 // open keychain, connect to all the CDSA modules we'll need
71
72 dlDbHand = keychain->database()->handle();
73 cspHand = keychain->csp()->handle();
74
75 tpHand = srTpStartup();
76 if (tpHand == 0)
77 MacOSError::throwMe(ioErr);
78
79 clHand = srClStartup();
80 if (clHand == 0)
81 MacOSError::throwMe(ioErr);
82
83 // generate key pair, private key stored in keychain
84 ortn = generateKeyPair(cspHand, dlDbHand, SR_KEY_ALGORITHM, SR_KEY_SIZE_IN_BITS,
85 "FileVault Master Password Key", &pubKey, &privKey);
86 if (ortn)
87 MacOSError::throwMe(ortn);
88
89 // generate the cert
90 ortn = createRootCert(tpHand,clHand,cspHand,pubKey,privKey,hostStr,userStr,
91 SR_CERT_SIGNATURE_ALGORITHM,&algOid,&certData);
92 if (ortn)
93 MacOSError::throwMe(ortn);
94
95 // store the cert in the same DL/DB as the key pair [see SecCertificateCreateFromData]
96
97 SecPointer<Certificate> certificatePtr(new Certificate(Required(&certData), CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER));
98 Required(&certRef) = certificatePtr->handle();
99
100 // Add the certificate item to the keychain [see SecCertificateAddToKeychain]
101 KeychainCore::Item item(Certificate::required(certRef));
102 keychain->add(item);
103
104 CFRelease(certRef);
105
106 // return the cert to caller
107 *cert = CFDataCreate(NULL, certData.Data, certData.Length);
108
109 // cleanup
110 if (hostStr)
111 free(hostStr);
112 if (userStr)
113 free(userStr);
114 if (tpHand)
115 CSSM_ModuleDetach(tpHand);
116 if (clHand)
117 CSSM_ModuleDetach(clHand);
118 if (pubKey)
119 {
120 CSSM_FreeKey(cspHand,
121 NULL, // access cred
122 pubKey,
123 CSSM_FALSE); // delete
124 APP_FREE(pubKey);
125 }
126 if (privKey)
127 {
128 CSSM_FreeKey(cspHand,
129 NULL, // access cred
130 privKey,
131 CSSM_FALSE); // delete
132 APP_FREE(privKey);
133 }
134
135 return ortn;
136 }
137
138 #pragma mark -------------------- SecFileVaultCert private implementation --------------------
139
140 OSStatus SecFileVaultCert::createRootCert(
141 CSSM_TP_HANDLE tpHand,
142 CSSM_CL_HANDLE clHand,
143 CSSM_CSP_HANDLE cspHand,
144 CSSM_KEY_PTR subjPubKey,
145 CSSM_KEY_PTR signerPrivKey,
146 const char *hostName, // CSSMOID_CommonName
147 const char *userName, // CSSMOID_Description
148 CSSM_ALGORITHMS sigAlg,
149 const CSSM_OID *sigOid,
150 CSSM_DATA_PTR certData) // mallocd and RETURNED
151 {
152 CE_DataAndType exts[2];
153 CE_DataAndType *extp = exts;
154 unsigned numExts;
155 CSSM_DATA refId; // mallocd by
156 // CSSM_TP_SubmitCredRequest
157 CSSM_APPLE_TP_CERT_REQUEST certReq;
158 CSSM_TP_REQUEST_SET reqSet;
159 sint32 estTime;
160 CSSM_BOOL confirmRequired;
161 CSSM_TP_RESULT_SET_PTR resultSet;
162 CSSM_ENCODED_CERT *encCert;
163 CSSM_APPLE_TP_NAME_OID subjectNames[2];
164 CSSM_TP_CALLERAUTH_CONTEXT CallerAuthContext;
165 CSSM_FIELD policyId;
166
167 numExts = 0;
168
169 certReq.challengeString = NULL;
170
171 /* KeyUsage extension */
172 extp->type = DT_KeyUsage;
173 extp->critical = CSSM_FALSE;
174 extp->extension.keyUsage = CE_KU_DigitalSignature |
175 CE_KU_KeyCertSign |
176 CE_KU_KeyEncipherment |
177 CE_KU_DataEncipherment;
178 extp++;
179 numExts++;
180
181 /* BasicConstraints */
182 extp->type = DT_BasicConstraints;
183 extp->critical = CSSM_TRUE;
184 extp->extension.basicConstraints.cA = CSSM_TRUE;
185 extp->extension.basicConstraints.pathLenConstraintPresent = CSSM_FALSE;
186 extp++;
187 numExts++;
188
189 /* name array */
190 subjectNames[0].string = hostName;
191 subjectNames[0].oid = &CSSMOID_CommonName;
192 subjectNames[1].string = userName;
193 subjectNames[1].oid = &CSSMOID_Description;
194
195 /* certReq */
196 certReq.cspHand = cspHand;
197 certReq.clHand = clHand;
198 randUint32(certReq.serialNumber); // random serial number
199 certReq.numSubjectNames = 2;
200 certReq.subjectNames = subjectNames;
201
202 certReq.numIssuerNames = 0; // root for now
203 certReq.issuerNames = NULL;
204 certReq.issuerNameX509 = NULL;
205 certReq.certPublicKey = subjPubKey;
206 certReq.issuerPrivateKey = signerPrivKey;
207 certReq.signatureAlg = sigAlg;
208 certReq.signatureOid = *sigOid;
209 certReq.notBefore = 0;
210 certReq.notAfter = 60 * 60 * 24 * 365; // seconds from now, one year
211 certReq.numExtensions = numExts;
212 certReq.extensions = exts;
213
214 reqSet.NumberOfRequests = 1;
215 reqSet.Requests = &certReq;
216
217 /* a CSSM_TP_CALLERAUTH_CONTEXT to specify an OID */
218 memset(&CallerAuthContext, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT));
219 memset(&policyId, 0, sizeof(CSSM_FIELD));
220 policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN;
221
222 CallerAuthContext.Policy.NumberOfPolicyIds = 1;
223 CallerAuthContext.Policy.PolicyIds = &policyId;
224
225 CSSM_RETURN crtn = CSSM_TP_SubmitCredRequest(tpHand,
226 NULL, // PreferredAuthority
227 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE,
228 &reqSet,
229 &CallerAuthContext,
230 &estTime,
231 &refId);
232
233 if(crtn) {
234 printError("***Error submitting credential request",
235 "CSSM_TP_SubmitCredRequest", crtn);
236 return crtn;
237 }
238 crtn = CSSM_TP_RetrieveCredResult(tpHand,
239 &refId,
240 NULL, // CallerAuthCredentials
241 &estTime,
242 &confirmRequired,
243 &resultSet);
244 if(crtn) {
245 printError("***Error retreiving credential request",
246 "CSSM_TP_RetrieveCredResult", crtn);
247 return crtn;
248 }
249 if(resultSet == NULL) {
250 printf("***CSSM_TP_RetrieveCredResult returned NULL result set.\n");
251 return ioErr;
252 }
253 encCert = (CSSM_ENCODED_CERT *)resultSet->Results;
254 *certData = encCert->CertBlob;
255
256 /* free resources allocated by TP */
257 APP_FREE(refId.Data);
258 APP_FREE(encCert);
259 APP_FREE(resultSet);
260 return noErr;
261 }
262
263 /* Convert a reference key to a raw key. */
264 CSSM_RETURN SecFileVaultCert::refKeyToRaw(
265 CSSM_CSP_HANDLE cspHand,
266 const CSSM_KEY *refKey,
267 CSSM_KEY_PTR rawKey) // RETURNED
268 {
269 CSSM_CC_HANDLE ccHand;
270 CSSM_RETURN crtn;
271 CSSM_ACCESS_CREDENTIALS creds;
272
273 memset(rawKey, 0, sizeof(CSSM_KEY));
274 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
275 crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
276 CSSM_ALGID_NONE,
277 CSSM_ALGMODE_NONE,
278 &creds, // passPhrase
279 NULL, // wrapping key
280 NULL, // init vector
281 CSSM_PADDING_NONE, // Padding
282 0, // Params
283 &ccHand);
284 if(crtn) {
285 printError("refKeyToRaw: context err",
286 "CSSM_CSP_CreateSymmetricContext", crtn);
287 return crtn;
288 }
289
290 crtn = CSSM_WrapKey(ccHand,
291 &creds,
292 refKey,
293 NULL, // DescriptiveData
294 rawKey);
295 if(crtn != CSSM_OK) {
296 printError("refKeyToRaw: wrap err", "CSSM_WrapKey", crtn);
297 return crtn;
298 }
299 CSSM_DeleteContext(ccHand);
300 return CSSM_OK;
301 }
302
303 /*
304 * Find private key by label, modify its Label attr to be the
305 * hash of the associated public key.
306 */
307 CSSM_RETURN SecFileVaultCert::setPubKeyHash(
308 CSSM_CSP_HANDLE cspHand,
309 CSSM_DL_DB_HANDLE dlDbHand,
310 const CSSM_KEY *pubOrPrivKey, // to get hash; raw or ref/CSPDL
311 const char *keyLabel) // look up by this
312 {
313 CSSM_QUERY query;
314 CSSM_SELECTION_PREDICATE predicate;
315 CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
316 CSSM_RETURN crtn;
317 CSSM_DATA labelData;
318 CSSM_HANDLE resultHand;
319
320 labelData.Data = (uint8 *)keyLabel;
321 labelData.Length = strlen(keyLabel) + 1; // incl. NULL
322 query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
323 query.Conjunctive = CSSM_DB_NONE;
324 query.NumSelectionPredicates = 1;
325 predicate.DbOperator = CSSM_DB_EQUAL;
326
327 predicate.Attribute.Info.AttributeNameFormat =
328 CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
329 predicate.Attribute.Info.Label.AttributeName = "Label";
330 predicate.Attribute.Info.AttributeFormat =
331 CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
332 predicate.Attribute.Value = &labelData;
333 query.SelectionPredicate = &predicate;
334
335 query.QueryLimits.TimeLimit = 0;
336 query.QueryLimits.SizeLimit = 1;
337 query.QueryFlags = 0;
338
339 /* build Record attribute with one attr */
340 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
341 CSSM_DB_ATTRIBUTE_DATA attr;
342 attr.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
343 attr.Info.Label.AttributeName = "Label";
344 attr.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
345
346 recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
347 recordAttrs.NumberOfAttributes = 1;
348 recordAttrs.AttributeData = &attr;
349
350 crtn = CSSM_DL_DataGetFirst(dlDbHand,
351 &query,
352 &resultHand,
353 &recordAttrs,
354 NULL, // hopefully optional ...theData,
355 &record);
356 /* abort only on success */
357 if(crtn != CSSM_OK) {
358 printError("***setPubKeyHash: can't find private key",
359 "CSSM_DL_DataGetFirst", crtn);
360 return crtn;
361 }
362
363 /*
364 * If specified key is a ref key, do NULL unwrap for use with raw CSP.
365 * If the CSPDL and SecurityServer support the key digest passthrough
366 * this is unnecessary.
367 */
368 CSSM_KEY rawKeyToDigest;
369 if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) {
370 crtn = refKeyToRaw(cspHand, pubOrPrivKey, &rawKeyToDigest);
371 if(crtn) {
372 printError("***Error converting public key to raw format",
373 "setPubKeyHash", crtn);
374 return crtn;
375 }
376 }
377 else {
378 /* use as is */
379 rawKeyToDigest = *pubOrPrivKey;
380 }
381
382 /* connect to raw CSP */
383 CSSM_CSP_HANDLE rawCspHand = srCspStartup(CSSM_TRUE);
384 if(rawCspHand == 0) {
385 printf("***Error connecting to raw CSP; aborting.\n");
386 return -1;
387 }
388
389 /* calculate hash of pub key from private or public part */
390 CSSM_DATA_PTR keyDigest = NULL;
391 CSSM_CC_HANDLE ccHand;
392 crtn = CSSM_CSP_CreatePassThroughContext(rawCspHand,
393 &rawKeyToDigest,
394 &ccHand);
395 if(ccHand == 0) {
396 printError("***Error calculating public key hash. Aborting:",
397 "CSSM_CSP_CreatePassThroughContext", crtn);
398 return -1;
399 }
400 crtn = CSSM_CSP_PassThrough(ccHand,
401 CSSM_APPLECSP_KEYDIGEST,
402 NULL,
403 (void **)&keyDigest);
404 if(crtn) {
405 printError("***Error calculating public key hash. Aborting:",
406 "CSSM_CSP_PassThrough(PUBKEYHASH)", crtn); // <<<<<<<<<<<<<<<<<<<
407 return crtn;
408 }
409 if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) {
410 /* created in refKeyToRaw().... */
411 CSSM_FreeKey(cspHand, NULL, &rawKeyToDigest, CSSM_FALSE);
412 }
413 CSSM_DeleteContext(ccHand);
414 CSSM_ModuleDetach(rawCspHand);
415
416 /*
417 * Replace Label attr data with hash.
418 * NOTE: the module which allocated this attribute data - a DL -
419 * was loaded and attached by the Sec layer, not by us. Thus
420 * we can't use the memory allocator functions *we* used when
421 * attaching to the CSPDL - we have to use the ones
422 * which the Sec layer registered with the DL.
423 */
424 CSSM_API_MEMORY_FUNCS memFuncs;
425 crtn = CSSM_GetAPIMemoryFunctions(dlDbHand.DLHandle, &memFuncs);
426 if(crtn) {
427 printError("***Error ", "CSSM_GetAPIMemoryFunctions(DLHandle)",
428 crtn);
429 /* oh well, leak and continue */
430 }
431 else {
432 memFuncs.free_func(attr.Value->Data, memFuncs.AllocRef);
433 memFuncs.free_func(attr.Value, memFuncs.AllocRef);
434 }
435 attr.Value = keyDigest;
436
437 /* modify key attributes */
438 crtn = CSSM_DL_DataModify(dlDbHand,
439 CSSM_DL_DB_RECORD_PRIVATE_KEY,
440 record,
441 &recordAttrs,
442 NULL, // DataToBeModified
443 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
444 if(crtn) {
445 printError("***Error setting public key hash. Aborting",
446 "CSSM_DL_DataModify(PUBKEYHASH)", crtn);
447 return crtn;
448 }
449 crtn = CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
450 if(crtn) {
451 printError("***Error while stopping query",
452 "CSSM_DL_DataAbortQuery", crtn);
453 /* let's keep going in this case */
454 }
455 crtn = CSSM_DL_FreeUniqueRecord(dlDbHand, record);
456 if(crtn) {
457 printError("***Error while freeing record",
458 "CSSM_DL_FreeUniqueRecord", crtn);
459 /* let's keep going in this case */
460 crtn = CSSM_OK;
461 }
462
463 /* free resources */
464 srAppFree(keyDigest->Data, NULL); //***
465 return CSSM_OK;
466 }
467
468 /*
469 * Generate a key pair using the CSPDL.
470 */
471 OSStatus SecFileVaultCert::generateKeyPair(
472 CSSM_CSP_HANDLE cspHand,
473 CSSM_DL_DB_HANDLE dlDbHand,
474 CSSM_ALGORITHMS keyAlg, // e.g., CSSM_ALGID_RSA
475 uint32 keySizeInBits,
476 const char *keyLabel, // C string
477 CSSM_KEY_PTR *pubKeyPtr, // mallocd, created, RETURNED
478 CSSM_KEY_PTR *privKeyPtr) // mallocd, created, RETURNED
479 {
480 CSSM_KEY_PTR pubKey = reinterpret_cast<CSSM_KEY_PTR>(
481 APP_MALLOC(sizeof(CSSM_KEY)));
482 CSSM_KEY_PTR privKey = reinterpret_cast<CSSM_KEY_PTR>(
483 APP_MALLOC(sizeof(CSSM_KEY)));
484 if((pubKey == NULL) || (privKey == NULL)) {
485 return memFullErr;
486 }
487
488 CSSM_RETURN crtn;
489 CSSM_KEYUSE pubKeyUse;
490 CSSM_KEYUSE privKeyUse;
491
492 pubKeyUse = CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_ENCRYPT |
493 CSSM_KEYUSE_WRAP;
494 privKeyUse = CSSM_KEYUSE_SIGN | CSSM_KEYUSE_DECRYPT |
495 CSSM_KEYUSE_UNWRAP;
496
497 crtn = srCspGenKeyPair(cspHand,
498 &dlDbHand,
499 keyAlg,
500 keyLabel,
501 strlen(keyLabel) + 1,
502 keySizeInBits,
503 pubKey,
504 pubKeyUse,
505 CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_RETURN_REF,
506 privKey,
507 privKeyUse,
508 CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_RETURN_REF |
509 CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE);
510
511 if(crtn) {
512 APP_FREE(pubKey);
513 APP_FREE(privKey);
514 return paramErr;
515 }
516
517 /* bind private key to cert by public key hash */
518 crtn = setPubKeyHash(cspHand,
519 dlDbHand,
520 pubKey,
521 keyLabel);
522 if(crtn) {
523 printError("***Error setting public key hash. Continuing at peril",
524 "setPubKeyHash", crtn);
525 }
526
527 *pubKeyPtr = pubKey;
528 *privKeyPtr = privKey;
529 return noErr;
530 }
531
532 #pragma mark -------------------- utility functions --------------------
533
534 void SecFileVaultCert::printError(const char *errDescription,const char *errLocation,OSStatus crtn)
535 {
536 int len = 1; // trailing NULL in any case
537 if(errDescription) {
538 len += strlen(errDescription);
539 }
540 if(errLocation) {
541 len += strlen(errLocation);
542 }
543 char *buf = (char *)malloc(len);
544 buf[0] = 0;
545 if(errDescription) {
546 strcpy(buf, errDescription);
547 }
548 if(errLocation) {
549 strcat(buf, errLocation);
550 }
551 cssmPerror(buf, crtn);
552 free(buf);
553 }
554
555 // Fill a uint32 with random data
556 void SecFileVaultCert::randUint32(uint32 &u)
557 {
558 int dev = open("/dev/random", O_RDONLY);
559 if(dev < 0) {
560 return;
561 }
562 read(dev, &u, sizeof(u));
563 close(dev);
564 }