]> git.saurik.com Git - apple/security.git/blob - SecurityTests/cspxutils/dbTool/dbCert.cpp
Security-57031.30.12.tar.gz
[apple/security.git] / SecurityTests / cspxutils / dbTool / dbCert.cpp
1 /*
2 * Copyright (c) 2003-2005,2008 Apple 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 * dbCert.cpp - import a possibly bad cert along with its private key
21 */
22
23 #include "dbCert.h"
24 #include <Security/Security.h>
25 #include <security_cdsa_utils/cuFileIo.h>
26 #include <security_cdsa_utils/cuDbUtils.h>
27 #include <security_cdsa_utils/cuCdsaUtils.h>
28 #include <security_cdsa_utils/cuPem.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32
33 /* Copied from clxutils/clAppUtils/tpUtils */
34 /* defined in SecKeychainAPIPriv.h */
35 static const int kSecAliasItemAttr = 'alis';
36
37 /* Macro to declare a CSSM_DB_SCHEMA_ATTRIBUTE_INFO */
38 #define SCHEMA_ATTR_INFO(id, name, type) \
39 { id, (char *)name, {0, NULL}, CSSM_DB_ATTRIBUTE_FORMAT_ ## type }
40
41 /* Too bad we can't get this from inside of the Security framework. */
42 static CSSM_DB_SCHEMA_ATTRIBUTE_INFO certSchemaAttrInfo[] =
43 {
44 SCHEMA_ATTR_INFO(kSecCertTypeItemAttr, "CertType", UINT32),
45 SCHEMA_ATTR_INFO(kSecCertEncodingItemAttr, "CertEncoding", UINT32),
46 SCHEMA_ATTR_INFO(kSecLabelItemAttr, "PrintName", BLOB),
47 SCHEMA_ATTR_INFO(kSecAliasItemAttr, "Alias", BLOB),
48 SCHEMA_ATTR_INFO(kSecSubjectItemAttr, "Subject", BLOB),
49 SCHEMA_ATTR_INFO(kSecIssuerItemAttr, "Issuer", BLOB),
50 SCHEMA_ATTR_INFO(kSecSerialNumberItemAttr, "SerialNumber", BLOB),
51 SCHEMA_ATTR_INFO(kSecSubjectKeyIdentifierItemAttr, "SubjectKeyIdentifier", BLOB),
52 SCHEMA_ATTR_INFO(kSecPublicKeyHashItemAttr, "PublicKeyHash", BLOB)
53 };
54 #define NUM_CERT_SCHEMA_ATTRS \
55 (sizeof(certSchemaAttrInfo) / sizeof(CSSM_DB_SCHEMA_ATTRIBUTE_INFO))
56
57 /* Macro to declare a CSSM_DB_SCHEMA_INDEX_INFO */
58 #define SCHEMA_INDEX_INFO(id, indexNum, indexType) \
59 { id, CSSM_DB_INDEX_ ## indexType, CSSM_DB_INDEX_ON_ATTRIBUTE }
60
61
62 static CSSM_DB_SCHEMA_INDEX_INFO certSchemaIndices[] =
63 {
64 SCHEMA_INDEX_INFO(kSecCertTypeItemAttr, 0, UNIQUE),
65 SCHEMA_INDEX_INFO(kSecIssuerItemAttr, 0, UNIQUE),
66 SCHEMA_INDEX_INFO(kSecSerialNumberItemAttr, 0, UNIQUE),
67 SCHEMA_INDEX_INFO(kSecCertTypeItemAttr, 1, NONUNIQUE),
68 SCHEMA_INDEX_INFO(kSecSubjectItemAttr, 2, NONUNIQUE),
69 SCHEMA_INDEX_INFO(kSecIssuerItemAttr, 3, NONUNIQUE),
70 SCHEMA_INDEX_INFO(kSecSerialNumberItemAttr, 4, NONUNIQUE),
71 SCHEMA_INDEX_INFO(kSecSubjectKeyIdentifierItemAttr, 5, NONUNIQUE),
72 SCHEMA_INDEX_INFO(kSecPublicKeyHashItemAttr, 6, NONUNIQUE)
73 };
74 #define NUM_CERT_INDICES \
75 (sizeof(certSchemaIndices) / sizeof(CSSM_DB_SCHEMA_INDEX_INFO))
76
77
78 CSSM_RETURN tpAddCertSchema(
79 CSSM_DL_DB_HANDLE dlDbHand)
80 {
81 return CSSM_DL_CreateRelation(dlDbHand,
82 CSSM_DL_DB_RECORD_X509_CERTIFICATE,
83 "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
84 NUM_CERT_SCHEMA_ATTRS,
85 certSchemaAttrInfo,
86 NUM_CERT_INDICES,
87 certSchemaIndices);
88 }
89
90
91 /* copied verbatim from certTool */
92
93 /*
94 * Find private key by label, modify its Label attr to be the
95 * hash of the associated public key.
96 */
97 static CSSM_RETURN setPubKeyHash(
98 CSSM_CSP_HANDLE cspHand,
99 CSSM_DL_DB_HANDLE dlDbHand,
100 const char *keyLabel, // look up by this
101 CSSM_DATA *rtnKeyDigest) // optionally RETURNED, if so,
102 // caller owns and must cuAppFree
103 {
104 CSSM_QUERY query;
105 CSSM_SELECTION_PREDICATE predicate;
106 CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
107 CSSM_RETURN crtn;
108 CSSM_DATA labelData;
109 CSSM_HANDLE resultHand;
110
111 labelData.Data = (uint8 *)keyLabel;
112 labelData.Length = strlen(keyLabel) + 1; // incl. NULL
113 query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
114 query.Conjunctive = CSSM_DB_NONE;
115 query.NumSelectionPredicates = 1;
116 predicate.DbOperator = CSSM_DB_EQUAL;
117
118 predicate.Attribute.Info.AttributeNameFormat =
119 CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
120 predicate.Attribute.Info.Label.AttributeName = (char *)"Label";
121 predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
122 /* hope this cast is OK */
123 predicate.Attribute.Value = &labelData;
124 query.SelectionPredicate = &predicate;
125
126 query.QueryLimits.TimeLimit = 0; // FIXME - meaningful?
127 query.QueryLimits.SizeLimit = 1; // FIXME - meaningful?
128 query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA; // FIXME - used?
129
130 /* build Record attribute with one attr */
131 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
132 CSSM_DB_ATTRIBUTE_DATA attr;
133 attr.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
134 attr.Info.Label.AttributeName = (char *)"Label";
135 attr.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
136
137 recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
138 recordAttrs.NumberOfAttributes = 1;
139 recordAttrs.AttributeData = &attr;
140
141 CSSM_DATA recordData = {0, NULL};
142 crtn = CSSM_DL_DataGetFirst(dlDbHand,
143 &query,
144 &resultHand,
145 &recordAttrs,
146 &recordData,
147 &record);
148 /* abort only on success */
149 if(crtn != CSSM_OK) {
150 cuPrintError("CSSM_DL_DataGetFirst", crtn);
151 return crtn;
152 }
153
154 CSSM_KEY_PTR keyToDigest = (CSSM_KEY_PTR)recordData.Data;
155 CSSM_DATA_PTR keyDigest = NULL;
156 CSSM_CC_HANDLE ccHand;
157 crtn = CSSM_CSP_CreatePassThroughContext(cspHand,
158 keyToDigest,
159 &ccHand);
160 if(crtn) {
161 cuPrintError("CSSM_CSP_CreatePassThroughContext", crtn);
162 return crtn;
163 }
164 crtn = CSSM_CSP_PassThrough(ccHand,
165 CSSM_APPLECSP_KEYDIGEST,
166 NULL,
167 (void **)&keyDigest);
168 if(crtn) {
169 cuPrintError("CSSM_CSP_PassThrough(PUBKEYHASH)", crtn);
170 return -1;
171 }
172 CSSM_FreeKey(cspHand, NULL, keyToDigest, CSSM_FALSE);
173 CSSM_DeleteContext(ccHand);
174
175 /*
176 * Replace Label attr data with hash.
177 * NOTE: the module which allocated this attribute data - a DL -
178 * was loaded and attached by the Sec layer, not by us. Thus
179 * we can't use the memory allocator functions *we* used when
180 * attaching to the CSPDL - we have to use the ones
181 * which the Sec layer registered with the DL.
182 */
183 CSSM_API_MEMORY_FUNCS memFuncs;
184 crtn = CSSM_GetAPIMemoryFunctions(dlDbHand.DLHandle, &memFuncs);
185 if(crtn) {
186 cuPrintError("CSSM_GetAPIMemoryFunctions(DLHandle)", crtn);
187 /* oh well, leak and continue */
188 }
189 else {
190 memFuncs.free_func(attr.Value->Data, memFuncs.AllocRef);
191 memFuncs.free_func(attr.Value, memFuncs.AllocRef);
192 }
193 attr.Value = keyDigest;
194
195 /* modify key attributes */
196 crtn = CSSM_DL_DataModify(dlDbHand,
197 CSSM_DL_DB_RECORD_PRIVATE_KEY,
198 record,
199 &recordAttrs,
200 NULL, // DataToBeModified
201 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
202 if(crtn) {
203 cuPrintError("CSSM_DL_DataModify(PUBKEYHASH)", crtn);
204 return crtn;
205 }
206 crtn = CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
207 if(crtn) {
208 cuPrintError("CSSM_DL_DataAbortQuery", crtn);
209 /* let's keep going in this case */
210 }
211 crtn = CSSM_DL_FreeUniqueRecord(dlDbHand, record);
212 if(crtn) {
213 cuPrintError("CSSM_DL_FreeUniqueRecord", crtn);
214 /* let's keep going in this case */
215 crtn = CSSM_OK;
216 }
217
218 /* free resources */
219 if(rtnKeyDigest) {
220 *rtnKeyDigest = *keyDigest;
221 }
222 else {
223 cuAppFree(keyDigest->Data, NULL);
224 /* FIXME - don't we have to free keyDigest itself? */
225 }
226 return CSSM_OK;
227 }
228
229 static CSSM_RETURN importPrivateKey(
230 CSSM_DL_DB_HANDLE dlDbHand,
231 CSSM_CSP_HANDLE cspHand,
232 const char *privKeyFileName,
233 CSSM_ALGORITHMS keyAlg,
234 CSSM_BOOL pemFormat, // of the file
235 CSSM_KEYBLOB_FORMAT keyFormat, // of the key blob itself, NONE means
236 // use default
237 CSSM_DATA *keyHash) // OPTIONALLY RETURNED - if so, caller
238 // owns and must cuAppFree()
239 {
240 unsigned char *derKey = NULL;
241 unsigned derKeyLen;
242 unsigned char *pemKey = NULL;
243 unsigned pemKeyLen;
244 CSSM_KEY wrappedKey;
245 CSSM_KEY unwrappedKey;
246 CSSM_ACCESS_CREDENTIALS creds;
247 CSSM_CC_HANDLE ccHand = 0;
248 CSSM_RETURN crtn;
249 CSSM_DATA labelData;
250 CSSM_KEYHEADER_PTR hdr = &wrappedKey.KeyHeader;
251 CSSM_DATA descData = {0, NULL};
252 CSSM_CSP_HANDLE rawCspHand = 0;
253 const char *privKeyLabel = NULL;
254
255 /*
256 * Validate specified format for clarity
257 */
258 switch(keyAlg) {
259 case CSSM_ALGID_RSA:
260 switch(keyFormat) {
261 case CSSM_KEYBLOB_RAW_FORMAT_NONE:
262 keyFormat = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; // default
263 break;
264 case CSSM_KEYBLOB_RAW_FORMAT_PKCS1:
265 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
266 break;
267 default:
268 printf("***RSA Private key must be in PKCS1 or PKCS8 "
269 "format\n");
270 return CSSMERR_CSSM_INTERNAL_ERROR;
271 }
272 privKeyLabel = "Imported RSA key";
273 break;
274 case CSSM_ALGID_DSA:
275 switch(keyFormat) {
276 case CSSM_KEYBLOB_RAW_FORMAT_NONE:
277 keyFormat = CSSM_KEYBLOB_RAW_FORMAT_OPENSSL;
278 // default
279 break;
280 case CSSM_KEYBLOB_RAW_FORMAT_FIPS186:
281 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL:
282 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
283 break;
284 default:
285 printf("***DSA Private key must be in openssl, FIPS186, "
286 "or PKCS8 format\n");
287 return CSSMERR_CSSM_INTERNAL_ERROR;
288 }
289 privKeyLabel = "Imported DSA key";
290 break;
291 case CSSM_ALGID_DH:
292 switch(keyFormat) {
293 case CSSM_KEYBLOB_RAW_FORMAT_NONE:
294 keyFormat = CSSM_KEYBLOB_RAW_FORMAT_PKCS8; // default
295 break;
296 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
297 break;
298 default:
299 printf("***Diffie-Hellman Private key must be in"
300 "PKCS8 format.\n");
301 return CSSMERR_CSSM_INTERNAL_ERROR;
302 }
303 privKeyLabel = "Imported Diffie-Hellman key";
304 break;
305 }
306 if(readFile(privKeyFileName, &pemKey, &pemKeyLen)) {
307 printf("***Error reading private key from file %s. Aborting.\n",
308 privKeyFileName);
309 return CSSMERR_CSSM_INTERNAL_ERROR;
310 }
311 /* subsequent errors to done: */
312 if(pemFormat) {
313 int rtn = pemDecode(pemKey, pemKeyLen, &derKey, &derKeyLen);
314 if(rtn) {
315 printf("***%s: Bad PEM formatting. Aborting.\n",
316 privKeyFileName);
317 crtn = CSSMERR_CSP_INVALID_KEY;
318 goto done;
319 }
320 }
321 else {
322 derKey = pemKey;
323 derKeyLen = pemKeyLen;
324 }
325
326 /* importing a raw key into the CSPDL involves a NULL unwrap */
327 memset(&unwrappedKey, 0, sizeof(CSSM_KEY));
328 memset(&wrappedKey, 0, sizeof(CSSM_KEY));
329
330 /* set up the imported key to look like a CSSM_KEY */
331 hdr->HeaderVersion = CSSM_KEYHEADER_VERSION;
332 hdr->BlobType = CSSM_KEYBLOB_RAW;
333 hdr->AlgorithmId = keyAlg;
334 hdr->KeyClass = CSSM_KEYCLASS_PRIVATE_KEY;
335 hdr->KeyAttr = CSSM_KEYATTR_EXTRACTABLE;
336 hdr->KeyUsage = CSSM_KEYUSE_ANY;
337 hdr->Format = keyFormat;
338 wrappedKey.KeyData.Data = derKey;
339 wrappedKey.KeyData.Length = derKeyLen;
340
341 /* get key size in bits from raw CSP */
342 rawCspHand = cuCspStartup(CSSM_TRUE);
343 if(rawCspHand == 0) {
344 printf("***Error attaching to CSP. Aborting.\n");
345 crtn = CSSMERR_CSSM_INTERNAL_ERROR;
346 goto done;
347 }
348 CSSM_KEY_SIZE keySize;
349 crtn = CSSM_QueryKeySizeInBits(rawCspHand, CSSM_INVALID_HANDLE, &wrappedKey, &keySize);
350 if(crtn) {
351 cuPrintError("CSSM_QueryKeySizeInBits",crtn);
352 goto done;
353 }
354 hdr->LogicalKeySizeInBits = keySize.LogicalKeySizeInBits;
355
356 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
357 crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
358 CSSM_ALGID_NONE, // unwrapAlg
359 CSSM_ALGMODE_NONE, // unwrapMode
360 &creds,
361 NULL, // unwrappingKey
362 NULL, // initVector
363 CSSM_PADDING_NONE, // unwrapPad
364 0, // Params
365 &ccHand);
366 if(crtn) {
367 cuPrintError("CSSM_CSP_CreateSymmetricContext", crtn);
368 goto done;
369 }
370
371 /* add DL/DB to context */
372 CSSM_CONTEXT_ATTRIBUTE newAttr;
373 newAttr.AttributeType = CSSM_ATTRIBUTE_DL_DB_HANDLE;
374 newAttr.AttributeLength = sizeof(CSSM_DL_DB_HANDLE);
375 newAttr.Attribute.Data = (CSSM_DATA_PTR)&dlDbHand;
376 crtn = CSSM_UpdateContextAttributes(ccHand, 1, &newAttr);
377 if(crtn) {
378 cuPrintError("CSSM_UpdateContextAttributes", crtn);
379 goto done;
380 }
381
382 /* do the NULL unwrap */
383 labelData.Data = (uint8 *)privKeyLabel;
384 labelData.Length = strlen(privKeyLabel) + 1;
385 crtn = CSSM_UnwrapKey(ccHand,
386 NULL, // PublicKey
387 &wrappedKey,
388 CSSM_KEYUSE_ANY,
389 CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT |
390 CSSM_KEYATTR_SENSITIVE |CSSM_KEYATTR_EXTRACTABLE,
391 &labelData,
392 NULL, // CredAndAclEntry
393 &unwrappedKey,
394 &descData); // required
395 if(crtn != CSSM_OK) {
396 cuPrintError("CSSM_UnwrapKey", crtn);
397 goto done;
398 }
399
400 /* one more thing: bind this private key to its public key */
401 crtn = setPubKeyHash(cspHand, dlDbHand, privKeyLabel, keyHash);
402
403 /* We don't need the unwrapped key any more */
404 CSSM_FreeKey(cspHand,
405 NULL, // access cred
406 &unwrappedKey,
407 CSSM_FALSE); // delete
408
409 done:
410 if(ccHand) {
411 CSSM_DeleteContext(ccHand);
412 }
413 if(derKey) {
414 free(derKey); // mallocd by readFile() */
415 }
416 if(pemFormat && pemKey) {
417 free(pemKey);
418 }
419 if(rawCspHand) {
420 CSSM_ModuleDetach(rawCspHand);
421 }
422 return crtn;
423 }
424
425
426 CSSM_RETURN importBadCert(
427 CSSM_DL_HANDLE dlHand,
428 const char *dbFileName,
429 const char *certFile,
430 const char *keyFile,
431 CSSM_ALGORITHMS keyAlg,
432 CSSM_BOOL pemFormat, // of the file
433 CSSM_KEYBLOB_FORMAT keyFormat, // of the key blob itself, NONE means
434 // use default
435 CSSM_BOOL verbose)
436 {
437 CSSM_DL_DB_HANDLE dlDbHand = {dlHand, 0};
438 CSSM_RETURN crtn;
439 CSSM_DATA keyDigest = {0, NULL};
440 CSSM_DATA certData = {0, NULL};
441 unsigned len;
442
443 CSSM_CSP_HANDLE cspHand = cuCspStartup(CSSM_FALSE);
444 if(cspHand == 0) {
445 printf("***Error attaching to CSPDL. Aborting.\n");
446 return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
447 }
448
449 /*
450 * 1. Open the (already existing) DB.
451 */
452 dlDbHand.DBHandle = cuDbStartupByName(dlHand,
453 (char *)dbFileName, // bogus non-const prototype
454 CSSM_FALSE, // do NOT create it
455 CSSM_FALSE); // quiet
456 if(dlDbHand.DBHandle == 0) {
457 printf("Error opening %s. Aborting.\n", dbFileName);
458 return CSSMERR_DL_DATASTORE_DOESNOT_EXIST;
459 }
460
461 /*
462 * Import key to DB, snagging its key digest along the way.
463 */
464 crtn = importPrivateKey(dlDbHand, cspHand,
465 keyFile, keyAlg, pemFormat, keyFormat,
466 &keyDigest);
467 if(crtn) {
468 printf("***Error importing key %s. Aborting.\n", keyFile);
469 goto errOut;
470 }
471
472 /*
473 * Now the cert.
474 */
475 if(readFile(certFile, &certData.Data, &len)) {
476 printf("***Error reading cert from %s. Aborting.\n", certFile);
477 goto errOut;
478 }
479 certData.Length = len;
480 crtn = cuAddCertToDb(dlDbHand, &certData,
481 CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
482 certFile, // printName
483 &keyDigest);
484 if(crtn == CSSMERR_DL_INVALID_RECORDTYPE) {
485 /* virgin DB, no cert schema: add schema and retry */
486 crtn = tpAddCertSchema(dlDbHand);
487 if(crtn == CSSM_OK) {
488 crtn = cuAddCertToDb(dlDbHand, &certData,
489 CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
490 certFile, // printName
491 &keyDigest);
492 }
493 }
494 if(crtn) {
495 printf("***Error importing cert %s. Aborting.\n", certFile);
496 }
497 errOut:
498 if(keyDigest.Data) {
499 cuAppFree(keyDigest.Data, NULL);
500 }
501 if(dlDbHand.DBHandle) {
502 CSSM_DL_DbClose(dlDbHand);
503 }
504 if(certData.Data) {
505 free(certData.Data);
506 }
507 return crtn;
508 }