]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
1 | /* |
2 | * cmstool.cpp - manipulate CMS messages, CMSEncoder/CMSDecoder version | |
3 | */ | |
4 | ||
5 | #include <Security/Security.h> | |
6 | #include <security_cdsa_utils/cuFileIo.h> | |
7 | #include <stdio.h> | |
8 | #include <stdlib.h> | |
9 | #include <string.h> | |
10 | #include <unistd.h> | |
11 | #include <utilLib/common.h> | |
12 | #include <security_cdsa_utils/cuFileIo.h> | |
13 | #include <security_cdsa_utils/cuPrintCert.h> | |
14 | #include <clAppUtils/identPicker.h> | |
15 | #include <clAppUtils/sslAppUtils.h> | |
16 | #include <security_cdsa_utils/cuOidParser.h> | |
17 | #include <CoreFoundation/CoreFoundation.h> | |
18 | #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h> | |
19 | ||
20 | #include <Security/SecTrustPriv.h> /* SecTrustGetCssmResultCode */ | |
21 | #include <Security/SecIdentityPriv.h> /* SecIdentityCreateWithCertificate */ | |
22 | #include <Security/CMSEncoder.h> | |
23 | #include <Security/CMSDecoder.h> | |
24 | #include <Security/CMSPrivate.h> | |
25 | ||
26 | #include <Security/SecCertificate.h> | |
27 | #include <Security/oidsattr.h> | |
28 | ||
29 | #define CFRELEASE(cfr) if(cfr != NULL) { CFRelease(cfr); } | |
30 | ||
31 | static SecKeychainRef keychain_open(const char *name); | |
32 | ||
33 | static void usage(char **argv) | |
34 | { | |
35 | printf("Usage: %s cmd [option ...]\n", argv[0]); | |
36 | printf("cmd values:\n"); | |
37 | printf(" sign -- create signedData\n"); | |
38 | printf(" envel -- create envelopedData\n"); | |
39 | printf(" signEnv -- create nested EnvelopedData(signedData(data))\n"); | |
40 | printf(" certs -- create certs-only CMS msg\n"); | |
41 | printf(" parse -- parse a CMS message file\n"); | |
42 | printf("Input/output options:\n"); | |
43 | printf(" -i infile\n"); | |
44 | printf(" -o outfile\n"); | |
45 | printf(" -D detachedContent -- detached content (parse only)\n"); | |
46 | printf(" -d detached -- infile contains detached content (sign only)\n"); | |
47 | printf(" -f certFileBase -- dump all certs to certFileBase\n"); | |
48 | printf("Signer and recipient options:\n"); | |
49 | printf(" -k keychain -- Keychain to search for certs\n"); | |
50 | printf(" -p -- Use identity picker\n"); | |
51 | printf(" -r recipient -- add recipient (via email address) of enveloped data\n"); | |
52 | printf(" -R recipCertFile -- add recipient (via cert from file) of enveloped data\n"); | |
53 | printf(" -S signerEmail -- add signer email address\n"); | |
54 | printf(" -C cert -- add (general) signedData cert\n"); | |
55 | printf("Misc. options:\n"); | |
56 | printf(" -e eContentType -- a(uthData)|r(keyData)\n"); | |
57 | printf(" -m -- multi updates; default is one-shot\n"); | |
58 | printf(" -1 (one) -- custom encoder/decoder\n"); | |
59 | printf(" -2 -- fetch SecCmsMessageRef\n"); | |
60 | printf(" -c -- parse signer certs\n"); | |
61 | printf(" -a [ceEt] -- Signed Attributes: c=SmimeCaps,\n"); | |
62 | printf(" e=EncrPrefs, E=MSEncrPrefs, t=signingTime\n"); | |
63 | printf(" -A anchorFile -- Verify certs using specified anchor cert\n"); | |
64 | printf(" -M -- Do SecTrustEvaluate manually\n"); | |
65 | printf(" -t certChainMode -- none|signer|chain|chainWithRoot; default is chain\n"); | |
66 | printf(" -l -- loop & pause for malloc debug\n"); | |
67 | printf(" -q -- quiet\n"); | |
68 | printf(" -Z -- silent, no output at all except for errors\n"); | |
69 | printf("Verification options:\n"); | |
70 | printf(" -v sign|encr|signEnv -- verify message is signed/encrypted/both\n"); | |
71 | printf(" -s numSigners -- verify msg has specified number of signers\n"); | |
72 | printf(" -E eContentType -- verify a(authData)|r(keyData)|d(data)\n"); | |
73 | printf(" -N numCerts -- verify number of certs\n"); | |
74 | exit(1); | |
75 | } | |
76 | ||
77 | /* high level op */ | |
78 | typedef enum { | |
79 | CTO_Sign, | |
80 | CTO_Envelop, | |
81 | CTO_SignEnvelop, | |
82 | CTO_CertsOnly, | |
83 | CTO_Parse | |
84 | } CT_Op; | |
85 | ||
86 | /* to verify */ | |
87 | typedef enum { | |
88 | CTV_None, | |
89 | CTV_Sign, | |
90 | CTV_Envelop, | |
91 | CTV_SignEnvelop | |
92 | } CT_Vfy; | |
93 | ||
94 | /* additional OIDS to specify as eContentType */ | |
95 | #define OID_PKINIT 0x2B, 6, 1, 5, 2, 3 | |
96 | #define OID_PKINIT_LEN 6 | |
97 | ||
98 | static const uint8 OID_PKINIT_AUTH_DATA[] = {OID_PKINIT, 1}; | |
99 | static const uint8 OID_PKINIT_DH_KEY_DATA[] = {OID_PKINIT, 2}; | |
100 | static const uint8 OID_PKINIT_RKEY_DATA[] = {OID_PKINIT, 3}; | |
101 | static const uint8 OID_PKINIT_KP_CLIENTAUTH[] = {OID_PKINIT, 3}; | |
102 | static const uint8 OID_PKINIT_KPKDC[] = {OID_PKINIT, 5}; | |
103 | ||
104 | static const CSSM_OID CSSMOID_PKINIT_AUTH_DATA = | |
105 | {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_AUTH_DATA}; | |
106 | static const CSSM_OID CSSMOID_PKINIT_DH_KEY_DATA = | |
107 | {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_DH_KEY_DATA}; | |
108 | static const CSSM_OID CSSMOID_PKINIT_RKEY_DATA = | |
109 | {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_RKEY_DATA}; | |
110 | static const CSSM_OID CSSMOID_PKINIT_KP_CLIENTAUTH = | |
111 | {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_KP_CLIENTAUTH}; | |
112 | static const CSSM_OID CSSMOID_PKINIT_KPKDC = | |
113 | {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_KPKDC}; | |
114 | ||
115 | /* | |
116 | * Find a cert in specified keychain or keychain list matching specified | |
117 | * email address. We happen to know that the email address is stored with the | |
118 | * kSecAlias attribute. | |
119 | */ | |
120 | static OSStatus findCert( | |
121 | const char *emailAddress, | |
122 | CFTypeRef kcArArray, // kc, array, or even NULL | |
123 | SecCertificateRef *cert) | |
124 | { | |
125 | OSStatus ortn; | |
126 | SecKeychainSearchRef srch; | |
127 | SecKeychainAttributeList attrList; | |
128 | SecKeychainAttribute attr; | |
129 | ||
130 | attr.tag = kSecAlias; | |
131 | attr.length = strlen(emailAddress); | |
132 | attr.data = (void *)emailAddress; | |
133 | attrList.count = 1; | |
134 | attrList.attr = &attr; | |
135 | ||
136 | ortn = SecKeychainSearchCreateFromAttributes(kcArArray, | |
137 | kSecCertificateItemClass, | |
138 | &attrList, | |
139 | &srch); | |
140 | if(ortn) { | |
141 | cssmPerror("SecKeychainSearchCreateFromAttributes", ortn); | |
142 | return ortn; | |
143 | } | |
144 | ||
145 | ortn = SecKeychainSearchCopyNext(srch, (SecKeychainItemRef *)cert); | |
146 | if(ortn) { | |
147 | printf("***No certs found matching recipient %s. Aborting.\n", | |
148 | emailAddress); | |
149 | return ortn; | |
150 | } | |
151 | CFRelease(srch); | |
152 | return noErr; | |
153 | } | |
154 | ||
155 | /* create a SecCertificateRef from a file */ | |
156 | static SecCertificateRef readCertFile( | |
157 | const char *fileName) | |
158 | { | |
159 | unsigned char *certData = NULL; | |
160 | unsigned certDataLen; | |
161 | SecCertificateRef rtnCert = NULL; | |
162 | ||
163 | if(readFile(fileName, &certData, &certDataLen)) { | |
164 | printf("***Error reading %s. Aborting.\n", fileName); | |
165 | return NULL; | |
166 | } | |
167 | CSSM_DATA cssmCert = {certDataLen, (uint8 *)certData}; | |
168 | OSStatus ortn = SecCertificateCreateFromData(&cssmCert, | |
169 | CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, | |
170 | &rtnCert); | |
171 | if(ortn) { | |
172 | cssmPerror("SecCertificateCreateFromData", ortn); | |
173 | printf("***Error creating cert fromn %s. Aborting.\n", fileName); | |
174 | } | |
175 | free(certData); | |
176 | return rtnCert; | |
177 | } | |
178 | ||
179 | static int dumpCertFiles( | |
180 | CFArrayRef allCerts, | |
181 | const char *fileBase, | |
182 | bool quiet) | |
183 | { | |
184 | char fileName[200]; | |
185 | ||
186 | if(allCerts == NULL) { | |
187 | printf("...no certs to write.\n"); | |
188 | return 0; | |
189 | } | |
190 | CFIndex numCerts = CFArrayGetCount(allCerts); | |
191 | if(numCerts == 0) { | |
192 | printf("...no certs to write.\n"); | |
193 | return 0; | |
194 | } | |
195 | for(CFIndex dex=0; dex<numCerts; dex++) { | |
196 | SecCertificateRef secCert = | |
197 | (SecCertificateRef)CFArrayGetValueAtIndex(allCerts, dex); | |
198 | CSSM_DATA certData; | |
199 | OSStatus ortn; | |
200 | ||
201 | ortn = SecCertificateGetData(secCert, &certData); | |
202 | if(ortn) { | |
203 | cssmPerror("SecCertificateGetData", ortn); | |
204 | return -1; | |
205 | } | |
206 | sprintf(fileName, "%s_%u.cer", fileBase, (unsigned)dex); | |
207 | if(writeFile(fileName, certData.Data, certData.Length)) { | |
208 | printf("***Error writing cert data to %s. Aborting.\n", fileName); | |
209 | return 1; | |
210 | } | |
211 | else if(!quiet) { | |
212 | printf("...wrote %u bytes to %s.\n", | |
213 | (unsigned)certData.Length, fileName); | |
214 | } | |
215 | } | |
216 | return 0; | |
217 | } | |
218 | ||
219 | /* | |
220 | * Do a random number of random-sized updates on a CMSEncoder. | |
221 | */ | |
222 | static OSStatus updateEncoder( | |
223 | CMSEncoderRef cmsEncoder, | |
224 | const unsigned char *inData, | |
225 | unsigned inDataLen) | |
226 | { | |
227 | unsigned toMove = inDataLen; | |
228 | unsigned thisMove; | |
229 | ||
230 | while(toMove != 0) { | |
231 | thisMove = genRand(1, toMove); | |
232 | OSStatus ortn = CMSEncoderUpdateContent(cmsEncoder, inData, thisMove); | |
233 | if(ortn) { | |
234 | cssmPerror("CMSEncoderUpdateContent", ortn); | |
235 | return ortn; | |
236 | } | |
237 | toMove -= thisMove; | |
238 | inData += thisMove; | |
239 | } | |
240 | return noErr; | |
241 | } | |
242 | ||
243 | /* | |
244 | * Do a random number of random-sized updates on a CMSDecoder. | |
245 | */ | |
246 | static OSStatus updateDecoder( | |
247 | CMSDecoderRef cmsDecoder, | |
248 | const unsigned char *inData, | |
249 | unsigned inDataLen) | |
250 | { | |
251 | unsigned toMove = inDataLen; | |
252 | unsigned thisMove; | |
253 | ||
254 | while(toMove != 0) { | |
255 | thisMove = genRand(1, toMove); | |
256 | OSStatus ortn = CMSDecoderUpdateMessage(cmsDecoder, inData, thisMove); | |
257 | if(ortn) { | |
258 | cssmPerror("CMSDecoderUpdateMessage", ortn); | |
259 | return ortn; | |
260 | } | |
261 | toMove -= thisMove; | |
262 | inData += thisMove; | |
263 | } | |
264 | return noErr; | |
265 | } | |
266 | ||
267 | #define TRUST_STRING_MAX 128 | |
268 | ||
269 | static OSStatus evalSecTrust( | |
270 | SecTrustRef secTrust, | |
271 | CFMutableArrayRef anchorArray, // optional | |
272 | char *trustStr, // caller-mallocd, TRUST_STRING_MAX chars | |
273 | bool quiet) | |
274 | { | |
275 | OSStatus ortn; | |
276 | SecTrustResultType secTrustResult; | |
277 | ||
278 | if(anchorArray) { | |
279 | ortn = SecTrustSetAnchorCertificates(secTrust, anchorArray); | |
280 | if(ortn) { | |
281 | /* should never happen */ | |
282 | cssmPerror("SecTrustSetAnchorCertificates", ortn); | |
283 | return ortn; | |
284 | } | |
285 | } | |
286 | ortn = SecTrustEvaluate(secTrust, &secTrustResult); | |
287 | if(ortn) { | |
288 | /* should never happen */ | |
289 | cssmPerror("SecTrustEvaluate", ortn); | |
290 | return ortn; | |
291 | } | |
292 | switch(secTrustResult) { | |
293 | case kSecTrustResultUnspecified: | |
294 | /* cert chain valid, no special UserTrust assignments */ | |
295 | case kSecTrustResultProceed: | |
296 | /* cert chain valid AND user explicitly trusts this */ | |
297 | sprintf(trustStr, "Successful\n"); | |
298 | return noErr; | |
299 | case kSecTrustResultDeny: | |
300 | case kSecTrustResultConfirm: | |
301 | /* | |
302 | * Cert chain may well have verified OK, but user has flagged | |
303 | * one of these certs as untrustable. | |
304 | */ | |
305 | sprintf(trustStr, "Not trusted per user-specified Trust level\n"); | |
306 | /* bogus return code I know */ | |
307 | return errSecInvalidTrustSetting; | |
308 | default: | |
309 | { | |
310 | /* get low-level TP error */ | |
311 | OSStatus tpStatus; | |
312 | ortn = SecTrustGetCssmResultCode(secTrust, &tpStatus); | |
313 | if(ortn) { | |
314 | cssmPerror("SecTrustGetCssmResultCode", ortn); | |
315 | return ortn; | |
316 | } | |
317 | switch(tpStatus) { | |
318 | case CSSMERR_TP_INVALID_ANCHOR_CERT: | |
319 | sprintf(trustStr, "Untrusted root\n"); | |
320 | break; | |
321 | case CSSMERR_TP_NOT_TRUSTED: | |
322 | /* no root, not even in implicit SSL roots */ | |
323 | sprintf(trustStr, "No root cert found\n"); | |
324 | break; | |
325 | case CSSMERR_TP_CERT_EXPIRED: | |
326 | sprintf(trustStr, "Expired cert\n"); | |
327 | break; | |
328 | case CSSMERR_TP_CERT_NOT_VALID_YET: | |
329 | sprintf(trustStr, "Cert not valid yet\n"); | |
330 | break; | |
331 | default: | |
332 | sprintf(trustStr, "Other cert failure (%s)", | |
333 | cssmErrToStr(tpStatus)); | |
334 | break; | |
335 | } | |
336 | return tpStatus; | |
337 | } | |
338 | } /* SecTrustEvaluate error */ | |
339 | /* NOT REACHED */ | |
340 | } | |
341 | ||
342 | static OSStatus doParse( | |
343 | const unsigned char *data, | |
344 | unsigned dataLen, | |
345 | const unsigned char *detachedContent, | |
346 | unsigned detachedContentLen, | |
347 | bool multiUpdate, | |
348 | CT_Vfy vfyOp, | |
349 | const CSSM_OID *eContentVfy, // optional to verify | |
350 | int numSignersVfy, // optional (>=0) to verify | |
351 | int numCertsVfy, // optionjal (>= 0) to verify | |
352 | bool parseSignerCert, | |
353 | const char *certFileBase, // optionally write certs here | |
354 | bool customDecoder, // invoke CMSDecoderSetDecoder() | |
355 | bool manTrustEval, // evaluate SecTrust ourself | |
356 | CFMutableArrayRef anchorArray, // optional, and only for manTrustEval | |
357 | bool quiet, | |
358 | CFDataRef *outData) // RETURNED | |
359 | { | |
360 | if((data == NULL) || (dataLen == 0)) { | |
361 | fprintf(stderr, "***Parse requires input file. Aborting.\n"); | |
362 | return paramErr; | |
363 | } | |
364 | ||
365 | CMSDecoderRef cmsDecoder; | |
366 | size_t numSigners; | |
367 | Boolean isEncrypted; | |
368 | CFArrayRef allCerts = NULL; | |
369 | unsigned signerDex; | |
370 | SecPolicyRef policy = NULL; | |
371 | SecPolicySearchRef policySearch = NULL; | |
372 | CFIndex numCerts = 0; | |
373 | int addDetachedAfterDecode = 0; | |
374 | SecArenaPoolRef arena = NULL; | |
375 | ||
376 | /* | |
377 | * Four different return codes: | |
378 | * -- ortn used for function returns; if nonzero, bail immediately and goto errOut | |
379 | * -- ourRtn is manually set per the output of CMSDecoderCopySignerStatus and | |
380 | * evalSecTrust | |
381 | * -- trustRtn is the output of manual SecTrustEvaluate (via evalSecTrust()) | |
382 | * -- vfyErr indicates mismatch in caller-specified error params | |
383 | * | |
384 | * All four have to be zero fgor us to return zero. | |
385 | */ | |
386 | OSStatus ourRtn = noErr; | |
387 | OSStatus ortn = noErr; | |
388 | OSStatus trustRtn = noErr; | |
389 | int vfyErr = 0; | |
390 | ||
391 | ortn = CMSDecoderCreate(&cmsDecoder); | |
392 | if(ortn) { | |
393 | cssmPerror("CMSDecoderCreate", ortn); | |
394 | return ortn; | |
395 | } | |
396 | ||
397 | /* subsequent errors to errOut: */ | |
398 | ||
399 | if(detachedContent != NULL) { | |
400 | /* | |
401 | * We can add detached content either before or after the | |
402 | * update/finalize; to test and verify, flip a coin to decide | |
403 | * when to do it. | |
404 | */ | |
405 | addDetachedAfterDecode = genRand(0, 1); | |
406 | if(!addDetachedAfterDecode) { | |
407 | CFDataRef cfDetach = CFDataCreate(NULL, detachedContent, detachedContentLen); | |
408 | ortn = CMSDecoderSetDetachedContent(cmsDecoder, cfDetach); | |
409 | CFRelease(cfDetach); | |
410 | if(ortn) { | |
411 | cssmPerror("CMSDecoderSetDetachedContent", ortn); | |
412 | goto errOut; | |
413 | } | |
414 | } | |
415 | } | |
416 | ||
417 | if(customDecoder) { | |
418 | /* Create a decoder; we don't have to free it, but we do have to free the | |
419 | * arena pool */ | |
420 | SecCmsDecoderRef coder = NULL; | |
421 | ||
422 | ortn = SecArenaPoolCreate(1024, &arena); | |
423 | if(ortn) { | |
424 | cssmPerror("SecArenaPoolCreate", ortn); | |
425 | goto errOut; | |
426 | } | |
427 | ortn = SecCmsDecoderCreate(arena, | |
428 | NULL, NULL, NULL, NULL, NULL, NULL, &coder); | |
429 | if(ortn) { | |
430 | cssmPerror("SecCmsDecoderCreate", ortn); | |
431 | goto errOut; | |
432 | } | |
433 | ortn = CMSDecoderSetDecoder(cmsDecoder, coder); | |
434 | if(ortn) { | |
435 | cssmPerror("CMSDecoderSetDecoder", ortn); | |
436 | goto errOut; | |
437 | } | |
438 | else if(!quiet) { | |
439 | printf("...set up custom SecCmsDecoderRef\n"); | |
440 | } | |
441 | } | |
442 | if(multiUpdate) { | |
443 | ortn = updateDecoder(cmsDecoder, data, dataLen); | |
444 | if(ortn) { | |
445 | goto errOut; | |
446 | } | |
447 | } | |
448 | else { | |
449 | ortn = CMSDecoderUpdateMessage(cmsDecoder, data, dataLen); | |
450 | if(ortn) { | |
451 | cssmPerror("CMSDecoderUpdateMessage", ortn); | |
452 | goto errOut; | |
453 | } | |
454 | } | |
455 | ortn = CMSDecoderFinalizeMessage(cmsDecoder); | |
456 | if(ortn) { | |
457 | cssmPerror("CMSDecoderFinalizeMessage", ortn); | |
458 | goto errOut; | |
459 | } | |
460 | if(addDetachedAfterDecode) { | |
461 | CFDataRef cfDetach = CFDataCreate(NULL, detachedContent, detachedContentLen); | |
462 | ortn = CMSDecoderSetDetachedContent(cmsDecoder, cfDetach); | |
463 | CFRelease(cfDetach); | |
464 | if(ortn) { | |
465 | cssmPerror("CMSDecoderSetDetachedContent", ortn); | |
466 | goto errOut; | |
467 | } | |
468 | } | |
469 | ortn = CMSDecoderGetNumSigners(cmsDecoder, &numSigners); | |
470 | if(ortn) { | |
471 | cssmPerror("CMSDecoderGetNumSigners", ortn); | |
472 | goto errOut; | |
473 | } | |
474 | ortn = CMSDecoderIsContentEncrypted(cmsDecoder, &isEncrypted); | |
475 | if(ortn) { | |
476 | cssmPerror("CMSDecoderIsContentEncrypted", ortn); | |
477 | goto errOut; | |
478 | } | |
479 | ortn = CMSDecoderCopyAllCerts(cmsDecoder, &allCerts); | |
480 | if(ortn) { | |
481 | cssmPerror("CMSDecoderCopyAllCerts", ortn); | |
482 | goto errOut; | |
483 | } | |
484 | if(allCerts) { | |
485 | numCerts = CFArrayGetCount(allCerts); | |
486 | } | |
487 | ||
488 | /* optional verify of expected message type */ | |
489 | switch(vfyOp) { | |
490 | case CTV_None: | |
491 | break; | |
492 | case CTV_Sign: | |
493 | if((numSigners == 0) && (allCerts == NULL)) { | |
494 | fprintf(stderr, "***Expected SignedData, but no signersFound\n"); | |
495 | vfyErr = 1; | |
496 | /* but keep going */ | |
497 | } | |
498 | if(isEncrypted) { | |
499 | fprintf(stderr, "***Expected SignedData, but msg IS encrypted\n"); | |
500 | vfyErr = 1; | |
501 | } | |
502 | break; | |
503 | case CTV_SignEnvelop: | |
504 | if(numSigners == 0) { | |
505 | fprintf(stderr, "***Expected Signed&Enveloped, but no signersFound\n"); | |
506 | vfyErr = 1; | |
507 | } | |
508 | if(!isEncrypted) { | |
509 | fprintf(stderr, "***Expected Signed&Enveloped, but msg not encrypted\n"); | |
510 | vfyErr = 1; | |
511 | } | |
512 | break; | |
513 | case CTV_Envelop: | |
514 | if(numSigners != 0) { | |
515 | fprintf(stderr, "***Expected EnvelopedData, but signers found\n"); | |
516 | vfyErr = 1; | |
517 | } | |
518 | if(!isEncrypted) { | |
519 | fprintf(stderr, "***Expected EnvelopedData, but msg not encrypted\n"); | |
520 | vfyErr = 1; | |
521 | } | |
522 | break; | |
523 | } | |
524 | ||
525 | if(numSignersVfy >= 0) { | |
526 | if((unsigned)numSignersVfy != numSigners) { | |
527 | fprintf(stderr, "***Expected %d signers; found %lu\n", | |
528 | numSignersVfy, numSigners); | |
529 | vfyErr = 1; | |
530 | } | |
531 | } | |
532 | if(numCertsVfy >= 0) { | |
533 | if((int)numCerts != numCertsVfy) { | |
534 | fprintf(stderr, "***Expected %d certs; found %d\n", | |
535 | numCertsVfy, (int)numCerts); | |
536 | vfyErr = 1; | |
537 | } | |
538 | } | |
539 | if(!quiet) { | |
540 | fprintf(stderr, "=== CMS message info ===\n"); | |
541 | fprintf(stderr, " Num Signers : %lu\n", (unsigned long)numSigners); | |
542 | fprintf(stderr, " Encrypted : %s\n", isEncrypted ? "true" : "false"); | |
543 | fprintf(stderr, " Num Certs : %lu\n", | |
544 | allCerts ? (unsigned long)CFArrayGetCount(allCerts) : 0); | |
545 | } | |
546 | ||
547 | if((certFileBase != NULL) & (allCerts != NULL)) { | |
548 | dumpCertFiles(allCerts, certFileBase, quiet); | |
549 | } | |
550 | ||
551 | ||
552 | if(numSigners) { | |
553 | CSSM_OID eContentType = {0, NULL}; | |
554 | CFDataRef eContentData = NULL; | |
555 | OidParser oidParser; | |
556 | char str[OID_PARSER_STRING_SIZE]; | |
557 | ||
558 | ortn = CMSDecoderCopyEncapsulatedContentType(cmsDecoder, &eContentData); | |
559 | if(ortn) { | |
560 | cssmPerror("CMSDecoderCopyEncapsulatedContentType", ortn); | |
561 | goto errOut; | |
562 | } | |
563 | if(eContentData != NULL) { | |
564 | eContentType.Data = (uint8 *)CFDataGetBytePtr(eContentData); | |
565 | eContentType.Length = CFDataGetLength(eContentData); | |
566 | } | |
567 | if(!quiet) { | |
568 | /* can't use stderr - oidparser is fixed w/stdout */ | |
569 | printf(" eContentType : "); | |
570 | if(eContentType.Data == NULL) { | |
571 | printf("***NONE FOUND***\n"); | |
572 | } | |
573 | else if(eContentType.Length == 0) { | |
574 | printf("***EMPTY***\n"); | |
575 | } | |
576 | else { | |
577 | oidParser.oidParse(eContentType.Data, eContentType.Length, str); | |
578 | printf("%s\n", str); | |
579 | } | |
580 | } | |
581 | ||
582 | if(eContentVfy != NULL) { | |
583 | if(eContentType.Data == NULL) { | |
584 | fprintf(stderr, "***Tried to verify eContentType, but none found\n"); | |
585 | vfyErr = 1; | |
586 | } | |
587 | else if(!appCompareCssmData(eContentVfy, &eContentType)) { | |
588 | fprintf(stderr, "***eContentType verify error\n"); | |
589 | fprintf(stderr, " Expected: "); | |
590 | oidParser.oidParse(eContentVfy->Data, eContentVfy->Length, str); | |
591 | printf("%s\n", str); | |
592 | fprintf(stderr, " Found : "); | |
593 | oidParser.oidParse(eContentType.Data, eContentType.Length, str); | |
594 | printf("%s\n", str); | |
595 | vfyErr = 1; | |
596 | } | |
597 | } | |
598 | CFRELEASE(eContentData); | |
599 | ||
600 | /* get a policy for cert evaluation */ | |
601 | ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3, | |
602 | &CSSMOID_APPLE_X509_BASIC, | |
603 | NULL, | |
604 | &policySearch); | |
605 | if(ortn) { | |
606 | cssmPerror("SecPolicySearchCreate", ortn); | |
607 | goto errOut; | |
608 | } | |
609 | ortn = SecPolicySearchCopyNext(policySearch, &policy); | |
610 | if(ortn) { | |
611 | cssmPerror("SecPolicySearchCopyNext", ortn); | |
612 | goto errOut; | |
613 | } | |
614 | } | |
615 | for(signerDex=0; signerDex<numSigners; signerDex++) { | |
616 | CMSSignerStatus signerStatus = kCMSSignerInvalidIndex; | |
617 | SecCertificateRef signerCert; | |
618 | CFStringRef signerEmailAddress; | |
619 | SecTrustRef secTrust; | |
620 | OSStatus certVerifyResultCode; | |
621 | char trustStr[TRUST_STRING_MAX]; | |
622 | ||
623 | ortn = CMSDecoderCopySignerStatus(cmsDecoder, signerDex, | |
624 | policy, | |
625 | manTrustEval ? FALSE : TRUE, /* evaluateSecTrust */ | |
626 | &signerStatus, | |
627 | &secTrust, | |
628 | &certVerifyResultCode); | |
629 | if(ortn) { | |
630 | cssmPerror("CMSDecoderCopySignerStatus", ortn); | |
631 | goto errOut; | |
632 | } | |
633 | if(ourRtn == noErr) { | |
634 | if((signerStatus != kCMSSignerValid) || | |
635 | (certVerifyResultCode != CSSM_OK)) { | |
636 | ourRtn = -1; | |
637 | } | |
638 | } | |
639 | ortn = CMSDecoderCopySignerEmailAddress(cmsDecoder, signerDex, | |
640 | &signerEmailAddress); | |
641 | if(ortn) { | |
642 | cssmPerror("CMSDecoderCopySignerEmailAddress", ortn); | |
643 | goto errOut; | |
644 | } | |
645 | ortn = CMSDecoderCopySignerCert(cmsDecoder, signerDex, | |
646 | &signerCert); | |
647 | if(ortn) { | |
648 | cssmPerror("CMSDecoderCopySignerCertificate", ortn); | |
649 | goto errOut; | |
650 | } | |
651 | if(manTrustEval) { | |
652 | trustRtn = evalSecTrust(secTrust, anchorArray, trustStr, quiet); | |
653 | } | |
654 | ||
655 | /* display nothing here if quiet true and status is copacetic */ | |
656 | if(!quiet || (signerStatus != kCMSSignerValid) || (trustRtn != noErr)) { | |
657 | fprintf(stderr, " Signer %u:\n", signerDex); | |
658 | fprintf(stderr, " signerStatus : "); | |
659 | switch(signerStatus) { | |
660 | case kCMSSignerUnsigned: | |
661 | fprintf(stderr, "kCMSSignerUnsigned\n"); break; | |
662 | case kCMSSignerValid: | |
663 | fprintf(stderr, "kCMSSignerValid\n"); break; | |
664 | case kCMSSignerNeedsDetachedContent: | |
665 | fprintf(stderr, "kCMSSignerNeedsDetachedContent\n"); break; | |
666 | case kCMSSignerInvalidSignature: | |
667 | fprintf(stderr, "kCMSSignerInvalidSignature\n"); break; | |
668 | case kCMSSignerInvalidCert: | |
669 | fprintf(stderr, "kCMSSignerInvalidCert\n"); break; | |
670 | case kCMSSignerInvalidIndex: | |
671 | fprintf(stderr, "kCMSSignerInvalidIndex\n"); break; | |
672 | } | |
673 | if(manTrustEval) { | |
674 | fprintf(stderr, " Trust Eval : %s\n", trustStr); | |
675 | } | |
676 | fprintf(stderr, " emailAddrs : "); | |
677 | if(signerEmailAddress == NULL) { | |
678 | fprintf(stderr, "<<none found>>\n"); | |
679 | } | |
680 | else { | |
681 | char emailStr[1000]; | |
682 | if(!CFStringGetCString(signerEmailAddress, | |
683 | emailStr, 1000, kCFStringEncodingASCII)) { | |
684 | fprintf(stderr, "<<<Error converting email address to C string>>>\n"); | |
685 | } | |
686 | else { | |
687 | fprintf(stderr, "%s\n", emailStr); | |
688 | } | |
689 | } | |
690 | ||
691 | fprintf(stderr, " vfyResult : %s\n", | |
692 | certVerifyResultCode ? | |
693 | cssmErrToStr(certVerifyResultCode) : "Success"); | |
694 | ||
695 | /* TBD: optionally manually verify the SecTrust object */ | |
696 | ||
697 | if(parseSignerCert) { | |
698 | ||
699 | if(signerCert == NULL) { | |
700 | fprintf(stderr, " <<<Unable to obtain signer cert>>>\n"); | |
701 | } | |
702 | else { | |
703 | CSSM_DATA certData; | |
704 | ortn = SecCertificateGetData(signerCert, &certData); | |
705 | if(ortn) { | |
706 | fprintf(stderr, " <<<Unable to obtain signer cert>>>\n"); | |
707 | cssmPerror("SecCertificateGetData", ortn); | |
708 | } | |
709 | else { | |
710 | printf("========== Signer Cert==========\n\n"); | |
711 | printCert(certData.Data, certData.Length, CSSM_FALSE); | |
712 | printf("========== End Signer Cert==========\n\n"); | |
713 | } | |
714 | } | |
715 | } /* parseSignerCert */ | |
716 | } /* displaying per-signer info */ | |
717 | ||
718 | CFRELEASE(signerCert); | |
719 | signerCert = NULL; | |
720 | CFRELEASE(signerEmailAddress); | |
721 | signerEmailAddress = NULL; | |
722 | CFRELEASE(secTrust); | |
723 | secTrust = NULL; | |
724 | } /* for signerDex */ | |
725 | ||
726 | if(ortn == noErr) { | |
727 | ortn = CMSDecoderCopyContent(cmsDecoder, outData); | |
728 | if(ortn) { | |
729 | cssmPerror("CMSDecoderCopyContent", ortn); | |
730 | } | |
731 | } | |
732 | ||
733 | errOut: | |
734 | CFRelease(cmsDecoder); | |
735 | if(arena != NULL) { | |
736 | SecArenaPoolFree(arena, false); | |
737 | } | |
738 | ||
739 | CFRELEASE(allCerts); | |
740 | CFRELEASE(policySearch); | |
741 | CFRELEASE(policy); | |
742 | if(ourRtn) { | |
743 | return ourRtn; | |
744 | } | |
745 | else if(trustRtn) { | |
746 | return trustRtn; | |
747 | } | |
748 | else if(vfyErr) { | |
749 | return vfyErr; | |
750 | } | |
751 | else { | |
752 | return ortn; | |
753 | } | |
754 | } | |
755 | ||
756 | static OSStatus doSign( | |
757 | CFTypeRef signerOrArray, | |
758 | const unsigned char *inData, | |
759 | unsigned inDataLen, | |
760 | bool multiUpdate, | |
761 | bool detachedContent, | |
762 | const CSSM_OID *eContentType, // OPTIONAL | |
763 | CMSSignedAttributes attrs, | |
764 | CFTypeRef otherCerts, // OPTIONAL | |
765 | bool customCoder, | |
766 | bool getCmsMsg, | |
767 | CMSCertificateChainMode chainMode, | |
768 | bool quiet, | |
769 | CFDataRef *outData) // RETURNED | |
770 | { | |
771 | if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) { | |
772 | fprintf(stderr, "***Sign requires input file. Aborting.\n"); | |
773 | return paramErr; | |
774 | } | |
775 | if(signerOrArray == NULL) { | |
776 | fprintf(stderr, "***Sign requires a signing identity. Aborting.\n"); | |
777 | return paramErr; | |
778 | } | |
779 | ||
780 | OSStatus ortn; | |
781 | CMSEncoderRef cmsEncoder = NULL; | |
782 | SecCmsMessageRef msg = NULL; /* for optional CMSEncoderGetCmsMessage */ | |
783 | SecArenaPoolRef arena = NULL; | |
784 | CSSM_DATA encoderOut = {0, NULL}; | |
785 | ||
786 | if(multiUpdate || otherCerts || getCmsMsg || customCoder || | |
787 | (chainMode != kCMSCertificateChain)) { | |
788 | /* one-shot encode doesn't support otherCerts or chainOptions*/ | |
789 | ||
790 | ortn = CMSEncoderCreate(&cmsEncoder); | |
791 | if(ortn) { | |
792 | cssmPerror("CMSEncoderCreate", ortn); | |
793 | return ortn; | |
794 | } | |
795 | /* subsequent errors to errOut: */ | |
796 | if(signerOrArray != NULL) { | |
797 | ortn = CMSEncoderAddSigners(cmsEncoder, signerOrArray); | |
798 | if(ortn) { | |
799 | goto errOut; | |
800 | } | |
801 | } | |
802 | if(eContentType) { | |
803 | ortn = CMSEncoderSetEncapsulatedContentType(cmsEncoder, eContentType); | |
804 | if(ortn) { | |
805 | goto errOut; | |
806 | } | |
807 | } | |
808 | if(detachedContent) { | |
809 | ortn = CMSEncoderSetHasDetachedContent(cmsEncoder, detachedContent); | |
810 | if(ortn) { | |
811 | goto errOut; | |
812 | } | |
813 | } | |
814 | if(otherCerts) { | |
815 | ortn = CMSEncoderAddSupportingCerts(cmsEncoder, otherCerts); | |
816 | if(ortn) { | |
817 | goto errOut; | |
818 | } | |
819 | } | |
820 | if(attrs) { | |
821 | ortn = CMSEncoderAddSignedAttributes(cmsEncoder, attrs); | |
822 | if(ortn) { | |
823 | goto errOut; | |
824 | } | |
825 | } | |
826 | if(chainMode != kCMSCertificateChain) { | |
827 | ortn = CMSEncoderSetCertificateChainMode(cmsEncoder, chainMode); | |
828 | if(ortn) { | |
829 | goto errOut; | |
830 | } | |
831 | } | |
832 | if(getCmsMsg || customCoder) { | |
833 | /* | |
834 | * We just want to trigger the state transition | |
835 | * that we know should happen. We also might need | |
836 | * the msg to create a custom coder. | |
837 | */ | |
838 | ortn = CMSEncoderGetCmsMessage(cmsEncoder, &msg); | |
839 | if(ortn) { | |
840 | cssmPerror("CMSEncoderGetCmsMessage", ortn); | |
841 | goto errOut; | |
842 | } | |
843 | } | |
844 | ||
845 | if(customCoder) { | |
846 | SecCmsEncoderRef coder = NULL; | |
847 | ortn = SecArenaPoolCreate(1024, &arena); | |
848 | if(ortn) { | |
849 | cssmPerror("SecArenaPoolCreate", ortn); | |
850 | goto errOut; | |
851 | } | |
852 | ortn = SecCmsEncoderCreate(msg, | |
853 | NULL, NULL, // no callback | |
854 | &encoderOut, // data goes here | |
855 | arena, | |
856 | NULL, NULL, // no password callback (right?) | |
857 | NULL, NULL, // decrypt key callback | |
858 | NULL, NULL, // detached digests | |
859 | &coder); | |
860 | if(ortn) { | |
861 | cssmPerror("SecCmsEncoderCreate", ortn); | |
862 | goto errOut; | |
863 | } | |
864 | ortn = CMSEncoderSetEncoder(cmsEncoder, coder); | |
865 | if(ortn) { | |
866 | cssmPerror("CMSEncoderSetEncoder", ortn); | |
867 | goto errOut; | |
868 | } | |
869 | else if(!quiet) { | |
870 | printf("...set up custom SecCmsEncoderRef\n"); | |
871 | } | |
872 | } | |
873 | /* random number of random-sized updates */ | |
874 | ortn = updateEncoder(cmsEncoder, inData, inDataLen); | |
875 | if(ortn) { | |
876 | goto errOut; | |
877 | } | |
878 | ||
879 | ortn = CMSEncoderCopyEncodedContent(cmsEncoder, outData); | |
880 | if(ortn) { | |
881 | cssmPerror("CMSEncoderCopyEncodedContent", ortn); | |
882 | } | |
883 | if(customCoder) { | |
884 | /* we have the data right here */ | |
885 | *outData = CFDataCreate(NULL, | |
886 | (const UInt8 *)encoderOut.Data, encoderOut.Length); | |
887 | } | |
888 | } | |
889 | else { | |
890 | ortn = CMSEncode(signerOrArray, | |
891 | NULL, /* recipients */ | |
892 | eContentType, | |
893 | detachedContent, | |
894 | attrs, | |
895 | inData, inDataLen, | |
896 | outData); | |
897 | if(ortn) { | |
898 | printf("***CMSEncode returned %ld\n", (long)ortn); | |
899 | cssmPerror("CMSEncode", ortn); | |
900 | } | |
901 | } | |
902 | errOut: | |
903 | if(cmsEncoder) { | |
904 | CFRelease(cmsEncoder); | |
905 | } | |
906 | if(arena) { | |
907 | SecArenaPoolFree(arena, false); | |
908 | } | |
909 | return ortn; | |
910 | } | |
911 | ||
912 | static OSStatus doEncrypt( | |
913 | CFTypeRef recipOrArray, | |
914 | const unsigned char *inData, | |
915 | unsigned inDataLen, | |
916 | bool multiUpdate, | |
917 | CFDataRef *outData) // RETURNED | |
918 | { | |
919 | if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) { | |
920 | fprintf(stderr, "***Encrypt requires input file. Aborting.\n"); | |
921 | return paramErr; | |
922 | } | |
923 | if(recipOrArray == NULL) { | |
924 | fprintf(stderr, "***Encrypt requires a recipient certificate. Aborting.\n"); | |
925 | return paramErr; | |
926 | } | |
927 | ||
928 | OSStatus ortn; | |
929 | CMSEncoderRef cmsEncoder = NULL; | |
930 | ||
931 | if(multiUpdate) { | |
932 | ortn = CMSEncoderCreate(&cmsEncoder); | |
933 | if(ortn) { | |
934 | cssmPerror("CMSEncoderCreate", ortn); | |
935 | return ortn; | |
936 | } | |
937 | /* subsequent errors to errOut: */ | |
938 | ortn = CMSEncoderAddRecipients(cmsEncoder, recipOrArray); | |
939 | if(ortn) { | |
940 | goto errOut; | |
941 | } | |
942 | ||
943 | /* random number of random-sized updates */ | |
944 | ortn = updateEncoder(cmsEncoder, inData, inDataLen); | |
945 | if(ortn) { | |
946 | goto errOut; | |
947 | } | |
948 | ortn = CMSEncoderCopyEncodedContent(cmsEncoder, outData); | |
949 | if(ortn) { | |
950 | cssmPerror("CMSEncoderCopyEncodedContent", ortn); | |
951 | } | |
952 | } | |
953 | else { | |
954 | /* one-shot */ | |
955 | ortn = CMSEncode(NULL, /* signers */ | |
956 | recipOrArray, | |
957 | NULL, /* eContentType */ | |
958 | FALSE, /* detachedContent */ | |
959 | kCMSAttrNone, | |
960 | inData, inDataLen, | |
961 | outData); | |
962 | if(ortn) { | |
963 | printf("***CMSEncode returned %ld\n", (long)ortn); | |
964 | cssmPerror("CMSEncode", ortn); | |
965 | } | |
966 | } | |
967 | errOut: | |
968 | if(cmsEncoder) { | |
969 | CFRelease(cmsEncoder); | |
970 | } | |
971 | return ortn; | |
972 | } | |
973 | ||
974 | /* create nested message: msg = EnvelopedData(SignedData(inData)) */ | |
975 | static OSStatus doSignEncrypt( | |
976 | CFTypeRef recipOrArray, // encryption recipients | |
977 | CFTypeRef signerOrArray, // signers | |
978 | const CSSM_OID *eContentType, // OPTIONAL - for signedData | |
979 | CMSSignedAttributes attrs, | |
980 | const unsigned char *inData, | |
981 | unsigned inDataLen, | |
982 | bool multiUpdate, | |
983 | CFTypeRef otherCerts, // OPTIONAL | |
984 | CFDataRef *outData) // RETURNED | |
985 | { | |
986 | if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) { | |
987 | fprintf(stderr, "***Sign/Encrypt requires input file. Aborting.\n"); | |
988 | return paramErr; | |
989 | } | |
990 | if(recipOrArray == NULL) { | |
991 | fprintf(stderr, "***Sign/Encrypt requires a recipient certificate. Aborting.\n"); | |
992 | return paramErr; | |
993 | } | |
994 | if(signerOrArray == NULL) { | |
995 | fprintf(stderr, "***Sign/Encrypt requires a signer Identity. Aborting.\n"); | |
996 | return paramErr; | |
997 | } | |
998 | ||
999 | OSStatus ortn; | |
1000 | CMSEncoderRef cmsEncoder = NULL; | |
1001 | ||
1002 | if(multiUpdate || otherCerts) { | |
1003 | ortn = CMSEncoderCreate(&cmsEncoder); | |
1004 | if(ortn) { | |
1005 | cssmPerror("CMSEncoderCreate", ortn); | |
1006 | return ortn; | |
1007 | } | |
1008 | /* subsequent errors to errOut: */ | |
1009 | ortn = CMSEncoderAddRecipients(cmsEncoder, recipOrArray); | |
1010 | if(ortn) { | |
1011 | goto errOut; | |
1012 | } | |
1013 | ortn = CMSEncoderAddSigners(cmsEncoder, signerOrArray); | |
1014 | if(ortn) { | |
1015 | goto errOut; | |
1016 | } | |
1017 | if(eContentType) { | |
1018 | ortn = CMSEncoderSetEncapsulatedContentType(cmsEncoder, eContentType); | |
1019 | if(ortn) { | |
1020 | goto errOut; | |
1021 | } | |
1022 | } | |
1023 | if(otherCerts) { | |
1024 | ortn = CMSEncoderAddSupportingCerts(cmsEncoder, otherCerts); | |
1025 | if(ortn) { | |
1026 | goto errOut; | |
1027 | } | |
1028 | } | |
1029 | if(attrs) { | |
1030 | ortn = CMSEncoderAddSignedAttributes(cmsEncoder, attrs); | |
1031 | if(ortn) { | |
1032 | goto errOut; | |
1033 | } | |
1034 | } | |
1035 | ||
1036 | /* random number of random-sized updates */ | |
1037 | ortn = updateEncoder(cmsEncoder, inData, inDataLen); | |
1038 | if(ortn) { | |
1039 | goto errOut; | |
1040 | } | |
1041 | ortn = CMSEncoderCopyEncodedContent(cmsEncoder, outData); | |
1042 | if(ortn) { | |
1043 | cssmPerror("CMSEncoderCopyEncodedContent", ortn); | |
1044 | } | |
1045 | } | |
1046 | else { | |
1047 | ortn = CMSEncode(signerOrArray, | |
1048 | recipOrArray, | |
1049 | eContentType, | |
1050 | FALSE, /* detachedContent */ | |
1051 | attrs, | |
1052 | inData, inDataLen, | |
1053 | outData); | |
1054 | if(ortn) { | |
1055 | printf("***CMSEncode returned %ld\n", (long)ortn); | |
1056 | cssmPerror("CMSEncode", ortn); | |
1057 | } | |
1058 | } | |
1059 | ||
1060 | errOut: | |
1061 | if(cmsEncoder) { | |
1062 | CFRelease(cmsEncoder); | |
1063 | } | |
1064 | return ortn; | |
1065 | } | |
1066 | ||
1067 | /* | |
1068 | * Create a CMS message containing only certs. | |
1069 | */ | |
1070 | static OSStatus makeCertBag( | |
1071 | CFTypeRef certsOrArray, | |
1072 | CFDataRef *outData) | |
1073 | { | |
1074 | if(certsOrArray == NULL) { | |
1075 | printf("***Need some certs to generate this type of message.\n"); | |
1076 | return -1; | |
1077 | } | |
1078 | ||
1079 | OSStatus ortn; | |
1080 | CMSEncoderRef cmsEncoder = NULL; | |
1081 | ||
1082 | ortn = CMSEncoderCreate(&cmsEncoder); | |
1083 | if(ortn) { | |
1084 | cssmPerror("CMSEncoderCreate", ortn); | |
1085 | return ortn; | |
1086 | } | |
1087 | /* subsequent errors to errOut: */ | |
1088 | ortn = CMSEncoderAddSupportingCerts(cmsEncoder, certsOrArray); | |
1089 | if(ortn) { | |
1090 | goto errOut; | |
1091 | } | |
1092 | ortn = CMSEncoderCopyEncodedContent(cmsEncoder, outData); | |
1093 | if(ortn) { | |
1094 | cssmPerror("CMSEncoderCopyEncodedContent", ortn); | |
1095 | } | |
1096 | errOut: | |
1097 | CFRelease(cmsEncoder); | |
1098 | return ortn; | |
1099 | } | |
1100 | ||
1101 | /* | |
1102 | * Support maintanance of single item or array of them. | |
1103 | * | |
1104 | * Given new incoming 'newThing': | |
1105 | * if both *thingArray and *currThing are NULL | |
1106 | * *currThing = newThing; | |
1107 | * done; | |
1108 | * create *thingArray; | |
1109 | * add *currThing to *thingArray if present; | |
1110 | * add newThing to *thingArray; | |
1111 | */ | |
1112 | static void addThing( | |
1113 | CFTypeRef newThing, | |
1114 | CFTypeRef *currThing, | |
1115 | CFMutableArrayRef *thingArray) | |
1116 | { | |
1117 | if((*currThing == NULL) && (*thingArray == NULL)) { | |
1118 | /* first occurrence of a thing */ | |
1119 | *currThing = newThing; | |
1120 | return; | |
1121 | } | |
1122 | ||
1123 | /* at least two things - prepare array */ | |
1124 | if(*thingArray == NULL) { | |
1125 | *thingArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
1126 | } | |
1127 | if(*currThing != NULL) { | |
1128 | /* move current thing to array */ | |
1129 | CFArrayAppendValue(*thingArray, *currThing); | |
1130 | CFRelease(*currThing); | |
1131 | *currThing = NULL; | |
1132 | } | |
1133 | CFArrayAppendValue(*thingArray, newThing); | |
1134 | CFRelease(newThing); | |
1135 | } | |
1136 | ||
1137 | int main(int argc, char **argv) | |
1138 | { | |
1139 | if(argc < 2) { | |
1140 | usage(argv); | |
1141 | } | |
1142 | ||
1143 | CT_Op op; | |
1144 | bool needId = false; | |
1145 | if(!strcmp(argv[1], "sign")) { | |
1146 | op = CTO_Sign; | |
1147 | needId = true; | |
1148 | } | |
1149 | else if(!strcmp(argv[1], "envel")) { | |
1150 | op = CTO_Envelop; | |
1151 | } | |
1152 | else if(!strcmp(argv[1], "signEnv")) { | |
1153 | op = CTO_SignEnvelop; | |
1154 | needId = true; | |
1155 | } | |
1156 | else if(!strcmp(argv[1], "certs")) { | |
1157 | op = CTO_CertsOnly; | |
1158 | } | |
1159 | else if(!strcmp(argv[1], "parse")) { | |
1160 | op = CTO_Parse; | |
1161 | } | |
1162 | else { | |
1163 | fprintf(stderr, "***Unrecognized cmd.\n"); | |
1164 | usage(argv); | |
1165 | } | |
1166 | ||
1167 | extern int optind; | |
1168 | extern char *optarg; | |
1169 | int arg; | |
1170 | OSStatus ortn; | |
1171 | ||
1172 | /* optional args */ | |
1173 | char *inFileName = NULL; | |
1174 | char *outFileName = NULL; | |
1175 | bool detachedContent = false; | |
1176 | char *detachedFile = NULL; | |
1177 | bool useIdPicker = false; | |
1178 | bool quiet = false; | |
1179 | bool silent = false; | |
1180 | bool parseSignerCert = false; | |
1181 | const CSSM_OID *eContentType = NULL; | |
1182 | bool multiUpdate = false; | |
1183 | bool loopPause = false; | |
1184 | char *certFileBase = NULL; | |
1185 | CMSSignedAttributes signedAttrs = 0; | |
1186 | char *anchorFile = NULL; | |
1187 | bool manTrustEval = false; | |
1188 | CMSCertificateChainMode chainMode = kCMSCertificateChain; | |
1189 | ||
1190 | /* for verification, usually in quiet/script mode */ | |
1191 | CT_Vfy vfyOp = CTV_None; | |
1192 | const CSSM_OID *eContentVfy = NULL; | |
1193 | int numSignersVfy = -1; | |
1194 | int numCertsVfy = -1; | |
1195 | ||
1196 | /* for verifying functions in CMSPrivate.h */ | |
1197 | bool customCoder = false; | |
1198 | bool fetchSecCmsMsg = false; | |
1199 | ||
1200 | /* | |
1201 | * Signer/recipient items and arrays - use one item if possible, | |
1202 | * else array (to test both paths) | |
1203 | */ | |
1204 | SecIdentityRef signerId = NULL; | |
1205 | CFMutableArrayRef signerArray = NULL; | |
1206 | SecCertificateRef recipCert = NULL; | |
1207 | CFMutableArrayRef recipArray = NULL; | |
1208 | SecCertificateRef generalCert = NULL; | |
1209 | CFMutableArrayRef generalCertArray = NULL; | |
1210 | SecKeychainRef kcRef = NULL; | |
1211 | CFMutableArrayRef anchorArray = NULL; | |
1212 | ||
1213 | optind = 2; | |
1214 | while ((arg = getopt(argc, argv, "i:o:k:pr:R:dD:e:mlqcv:s:E:S:C:f:N:a:A:M12t:Z")) != -1) { | |
1215 | switch (arg) { | |
1216 | case 'i': | |
1217 | inFileName = optarg; | |
1218 | break; | |
1219 | case 'o': | |
1220 | outFileName = optarg; | |
1221 | break; | |
1222 | case 'k': | |
1223 | kcRef = keychain_open(optarg); | |
1224 | if(!kcRef) { | |
1225 | // cssmPerror("SecKeychainOpen", ortn); | |
1226 | exit(1); | |
1227 | } | |
1228 | break; | |
1229 | case 'p': | |
1230 | useIdPicker = true; | |
1231 | break; | |
1232 | case 'r': | |
1233 | { | |
1234 | SecCertificateRef newCert = NULL; | |
1235 | char *recipient = optarg; | |
1236 | ortn = findCert(recipient, kcRef, &newCert); | |
1237 | if(ortn) { | |
1238 | exit(1); | |
1239 | } | |
1240 | addThing(newCert, (CFTypeRef *)&recipCert, &recipArray); | |
1241 | break; | |
1242 | } | |
1243 | case 'R': | |
1244 | { | |
1245 | SecCertificateRef certRef = readCertFile(optarg); | |
1246 | if(certRef == NULL) { | |
1247 | exit(1); | |
1248 | } | |
1249 | addThing(certRef, (CFTypeRef *)&recipCert, &recipArray); | |
1250 | break; | |
1251 | } | |
1252 | case 'S': | |
1253 | { | |
1254 | SecIdentityRef newId = NULL; | |
1255 | SecCertificateRef newCert = NULL; | |
1256 | char *signerEmail = optarg; | |
1257 | ||
1258 | /* | |
1259 | * first find the cert, optionally in the keychain already | |
1260 | * specified via -k | |
1261 | */ | |
1262 | ortn = findCert(signerEmail, kcRef, &newCert); | |
1263 | if(ortn) { | |
1264 | exit(1); | |
1265 | } | |
1266 | ||
1267 | /* map cert to an identity */ | |
1268 | ortn = SecIdentityCreateWithCertificate(kcRef, newCert, &newId); | |
1269 | if(ortn) { | |
1270 | cssmPerror("SecIdentityCreateWithCertificate", ortn); | |
1271 | exit(1); | |
1272 | } | |
1273 | ||
1274 | addThing(newId, (CFTypeRef *)&signerId, &signerArray); | |
1275 | CFRelease(newCert); | |
1276 | break; | |
1277 | } | |
1278 | case 'C': | |
1279 | { | |
1280 | SecCertificateRef newCert = readCertFile(optarg); | |
1281 | if(newCert == NULL) { | |
1282 | exit(1); | |
1283 | } | |
1284 | addThing(newCert, (CFTypeRef *)&generalCert, &generalCertArray); | |
1285 | break; | |
1286 | } | |
1287 | case 'c': | |
1288 | parseSignerCert = true; | |
1289 | break; | |
1290 | case 'v': | |
1291 | if(!strcmp(optarg, "sign")) { | |
1292 | vfyOp = CTV_Sign; | |
1293 | } | |
1294 | else if(!strcmp(optarg, "encr")) { | |
1295 | vfyOp = CTV_Envelop; | |
1296 | } | |
1297 | else if(!strcmp(optarg, "signEnv")) { | |
1298 | vfyOp = CTV_SignEnvelop; | |
1299 | } | |
1300 | else { | |
1301 | usage(argv); | |
1302 | } | |
1303 | break; | |
1304 | case 'e': | |
1305 | switch(optarg[0]) { | |
1306 | case 'a': | |
1307 | eContentType = &CSSMOID_PKINIT_AUTH_DATA; | |
1308 | break; | |
1309 | case 'r': | |
1310 | eContentType = &CSSMOID_PKINIT_RKEY_DATA; | |
1311 | break; | |
1312 | default: | |
1313 | usage(argv); | |
1314 | } | |
1315 | break; | |
1316 | case 'E': | |
1317 | switch(optarg[0]) { | |
1318 | case 'a': | |
1319 | eContentVfy = &CSSMOID_PKINIT_AUTH_DATA; | |
1320 | break; | |
1321 | case 'r': | |
1322 | eContentVfy = &CSSMOID_PKINIT_RKEY_DATA; | |
1323 | break; | |
1324 | case 'd': | |
1325 | eContentVfy = &CSSMOID_PKCS7_Data; | |
1326 | break; | |
1327 | default: | |
1328 | usage(argv); | |
1329 | } | |
1330 | break; | |
1331 | case 'd': | |
1332 | if(op != CTO_Sign) { | |
1333 | printf("-d only valid for op sign\n"); | |
1334 | exit(1); | |
1335 | } | |
1336 | detachedContent = true; | |
1337 | break; | |
1338 | case 'D': | |
1339 | if(op != CTO_Parse) { | |
1340 | printf("-D only valid for op sign\n"); | |
1341 | exit(1); | |
1342 | } | |
1343 | detachedFile = optarg; | |
1344 | break; | |
1345 | case 'l': | |
1346 | loopPause = true; | |
1347 | break; | |
1348 | case 'm': | |
1349 | multiUpdate = true; | |
1350 | break; | |
1351 | case 's': | |
1352 | numSignersVfy = atoi(optarg); | |
1353 | break; | |
1354 | case 'f': | |
1355 | certFileBase = optarg; | |
1356 | break; | |
1357 | case 'N': | |
1358 | numCertsVfy = atoi(optarg); | |
1359 | break; | |
1360 | case '1': | |
1361 | customCoder = true; | |
1362 | break; | |
1363 | case '2': | |
1364 | fetchSecCmsMsg = true; | |
1365 | break; | |
1366 | case 'a': | |
1367 | for(; *optarg; optarg++) { | |
1368 | switch(*optarg) { | |
1369 | case 'c': | |
1370 | signedAttrs |= kCMSAttrSmimeCapabilities; | |
1371 | break; | |
1372 | case 'e': | |
1373 | signedAttrs |= kCMSAttrSmimeEncryptionKeyPrefs; | |
1374 | break; | |
1375 | case 'E': | |
1376 | signedAttrs |= kCMSAttrSmimeMSEncryptionKeyPrefs; | |
1377 | break; | |
1378 | case 't': | |
1379 | signedAttrs |= kCMSAttrSigningTime; | |
1380 | break; | |
1381 | default: | |
1382 | usage(argv); | |
1383 | } | |
1384 | } | |
1385 | break; | |
1386 | case 'A': | |
1387 | anchorFile = optarg; | |
1388 | break; | |
1389 | case 'M': | |
1390 | manTrustEval = true; | |
1391 | break; | |
1392 | case 't': | |
1393 | if(!strcmp(optarg, "none")) { | |
1394 | chainMode = kCMSCertificateNone; | |
1395 | } | |
1396 | else if(!strcmp(optarg, "signer")) { | |
1397 | chainMode = kCMSCertificateSignerOnly; | |
1398 | } | |
1399 | else if(!strcmp(optarg, "chain")) { | |
1400 | chainMode = kCMSCertificateChain; | |
1401 | } | |
1402 | else if(!strcmp(optarg, "chainWithRoot")) { | |
1403 | chainMode = kCMSCertificateChainWithRoot; | |
1404 | } | |
1405 | else { | |
1406 | printf("***Bogus cert chain spec***\n"); | |
1407 | usage(argv); | |
1408 | } | |
1409 | break; | |
1410 | case 'q': | |
1411 | quiet = true; | |
1412 | break; | |
1413 | case 'Z': | |
1414 | quiet = true; | |
1415 | silent = true; | |
1416 | break; | |
1417 | default: | |
1418 | case '?': | |
1419 | usage(argv); | |
1420 | } | |
1421 | } | |
1422 | if(optind != argc) { | |
1423 | /* getopt does not return '?' */ | |
1424 | usage(argv); | |
1425 | } | |
1426 | ||
1427 | unsigned char *inData = NULL; | |
1428 | unsigned inDataLen = 0; | |
1429 | unsigned char *detachedData = NULL; | |
1430 | unsigned detachedDataLen = 0; | |
1431 | CFDataRef outData = NULL; | |
1432 | CFIndex byteCount = 0; | |
1433 | if(!silent) { | |
1434 | testStartBanner((char *)"newCmsTool", argc, argv); | |
1435 | } | |
1436 | ||
1437 | if(inFileName) { | |
1438 | if(readFile(inFileName, &inData, &inDataLen)) { | |
1439 | fprintf(stderr, "***Error reading infile %s. Aborting.\n", inFileName); | |
1440 | exit(1); | |
1441 | } | |
1442 | } | |
1443 | if(detachedFile) { | |
1444 | if(readFile(detachedFile, &detachedData, &detachedDataLen)) { | |
1445 | fprintf(stderr, "***Error reading detachedFile %s. Aborting.\n", detachedFile); | |
1446 | exit(1); | |
1447 | } | |
1448 | } | |
1449 | ||
1450 | /* signer IDs */ | |
1451 | if(useIdPicker) { | |
1452 | ortn = sslSimpleIdentPicker(kcRef, &signerId); | |
1453 | if(ortn) { | |
1454 | fprintf(stderr, "***Error obtaining identity via picker. Aborting.\n"); | |
1455 | exit(1); | |
1456 | } | |
1457 | } | |
1458 | if(anchorFile) { | |
1459 | SecCertificateRef secCert = readCertFile(anchorFile); | |
1460 | if(secCert == NULL) { | |
1461 | exit(1); | |
1462 | } | |
1463 | anchorArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
1464 | CFArrayAppendValue(anchorArray, secCert); | |
1465 | CFRelease(secCert); | |
1466 | } | |
1467 | ||
1468 | /* | |
1469 | * In order for signed blobs to contain a full cert chain, and to find | |
1470 | * certs matching a specific recipient cert email address, the keychain | |
1471 | * containing intermediates must be in the user's keychain search list | |
1472 | * (or, alternatively, the intermedaite certs must be added manually). | |
1473 | * To alleviate the burden on test scripts, we'll ensure that an optionally | |
1474 | * specified keychain is in factin the search list when we sign a message | |
1475 | * here. | |
1476 | * Careful - make sure to ALWAYS restore the search list! | |
1477 | */ | |
1478 | CFArrayRef originalSearchList = NULL; | |
1479 | if(kcRef != NULL) { | |
1480 | ortn = SecKeychainCopySearchList(&originalSearchList); | |
1481 | if(ortn) { | |
1482 | cssmPerror("SecKeychainCopySearchList", ortn); | |
1483 | exit(1); | |
1484 | } | |
1485 | CFMutableArrayRef newList = CFArrayCreateMutableCopy( | |
1486 | NULL, 0, originalSearchList); | |
1487 | CFArrayAppendValue(newList, kcRef); | |
1488 | ortn = SecKeychainSetSearchList(newList); | |
1489 | if(ortn) { | |
1490 | cssmPerror("SecKeychainSetSearchList", ortn); | |
1491 | exit(1); | |
1492 | } | |
1493 | /* DO NOT EXIT WITHOUT RESTORING TO originalSearchList */ | |
1494 | } | |
1495 | do { | |
1496 | switch(op) { | |
1497 | case CTO_Sign: | |
1498 | ortn = doSign((signerArray != NULL) ? | |
1499 | (CFTypeRef)signerArray : (CFTypeRef)signerId, | |
1500 | inData, inDataLen, | |
1501 | multiUpdate, detachedContent, eContentType, signedAttrs, | |
1502 | (generalCertArray != NULL) ? | |
1503 | (CFTypeRef)generalCertArray : (CFTypeRef)generalCert, | |
1504 | customCoder, fetchSecCmsMsg, chainMode, quiet, | |
1505 | &outData); | |
1506 | break; | |
1507 | case CTO_Envelop: | |
1508 | ortn = doEncrypt(recipArray ? | |
1509 | (CFTypeRef)recipArray : (CFTypeRef)recipCert, | |
1510 | inData, inDataLen, | |
1511 | multiUpdate, &outData); | |
1512 | break; | |
1513 | case CTO_SignEnvelop: | |
1514 | ortn = doSignEncrypt(recipArray ? | |
1515 | (CFTypeRef)recipArray : (CFTypeRef)recipCert, | |
1516 | signerArray ? | |
1517 | (CFTypeRef)signerArray : (CFTypeRef)signerId, | |
1518 | eContentType, signedAttrs, | |
1519 | inData, inDataLen, multiUpdate, | |
1520 | (generalCertArray != NULL) ? | |
1521 | (CFTypeRef)generalCertArray : (CFTypeRef)generalCert, | |
1522 | &outData); | |
1523 | break; | |
1524 | case CTO_CertsOnly: | |
1525 | ortn = makeCertBag((generalCertArray != NULL) ? | |
1526 | (CFTypeRef)generalCertArray : (CFTypeRef)generalCert, | |
1527 | &outData); | |
1528 | break; | |
1529 | case CTO_Parse: | |
1530 | ortn = doParse(inData, inDataLen, | |
1531 | detachedData, detachedDataLen, | |
1532 | multiUpdate, | |
1533 | vfyOp, eContentVfy, numSignersVfy, numCertsVfy, | |
1534 | parseSignerCert, certFileBase, customCoder, | |
1535 | manTrustEval, anchorArray, | |
1536 | quiet, &outData); | |
1537 | break; | |
1538 | } | |
1539 | ||
1540 | if(loopPause) { | |
1541 | if(outData) { | |
1542 | printf("...generated %u bytes of data.\n", | |
1543 | (unsigned)CFDataGetLength(outData)); | |
1544 | } | |
1545 | fpurge(stdin); | |
1546 | printf("q to quit, anything else to loop again: "); | |
1547 | char resp = getchar(); | |
1548 | if(resp == 'q') { | |
1549 | break; | |
1550 | } | |
1551 | else { | |
1552 | CFRELEASE(outData); | |
1553 | outData = NULL; | |
1554 | } | |
1555 | } | |
1556 | } while (loopPause); | |
1557 | ||
1558 | if(originalSearchList) { | |
1559 | ortn = SecKeychainSetSearchList(originalSearchList); | |
1560 | if(ortn) { | |
1561 | cssmPerror("SecKeychainSetSearchList", ortn); | |
1562 | /* keep going */ | |
1563 | } | |
1564 | } | |
1565 | ||
1566 | if(ortn) { | |
1567 | goto errOut; | |
1568 | } | |
1569 | ||
1570 | byteCount = outData ? CFDataGetLength(outData) : 0; | |
1571 | if(outData && outFileName) { | |
1572 | if(writeFile(outFileName, CFDataGetBytePtr(outData), byteCount)) { | |
1573 | fprintf(stderr, "***Error writing to %s.\n", outFileName); | |
1574 | ortn = -1; | |
1575 | } | |
1576 | else { | |
1577 | if(!quiet) { | |
1578 | fprintf(stderr, "...wrote %u bytes to %s.\n", | |
1579 | (unsigned)byteCount, outFileName); | |
1580 | } | |
1581 | } | |
1582 | } | |
1583 | else if(byteCount) { | |
1584 | fprintf(stderr, "...generated %u bytes but no place to write it.\n", | |
1585 | (unsigned)byteCount); | |
1586 | } | |
1587 | else if(outFileName) { | |
1588 | fprintf(stderr, "...nothing to write to file %s.\n", outFileName); | |
1589 | /* assume this is an error, caller wanted something */ | |
1590 | ortn = -1; | |
1591 | } | |
1592 | errOut: | |
1593 | return ortn; | |
1594 | } | |
1595 | ||
1596 | /* | |
1597 | From SecurityTool/keychain_utilites.cpp | |
1598 | This properly supports dynamic keychains, i.e. smartcards | |
1599 | */ | |
1600 | ||
1601 | SecKeychainRef keychain_open(const char *name) | |
1602 | { | |
1603 | SecKeychainRef keychain = NULL; | |
1604 | OSStatus result; | |
1605 | ||
1606 | // check_obsolete_keychain(name); | |
1607 | if (name && name[0] != '/') | |
1608 | { | |
1609 | CFArrayRef dynamic = NULL; | |
1610 | result = SecKeychainCopyDomainSearchList( | |
1611 | kSecPreferencesDomainDynamic, &dynamic); | |
1612 | if (result) | |
1613 | { | |
1614 | // cssmPerror("SecKeychainOpen", ortn); | |
1615 | // sec_error("SecKeychainCopyDomainSearchList %s: %s", | |
1616 | // name, sec_errstr(result)); | |
1617 | cssmPerror("SecKeychainCopyDomainSearchList", result); | |
1618 | return NULL; | |
1619 | } | |
1620 | else | |
1621 | { | |
1622 | uint32_t i; | |
1623 | uint32_t count = dynamic ? CFArrayGetCount(dynamic) : 0; | |
1624 | ||
1625 | for (i = 0; i < count; ++i) | |
1626 | { | |
1627 | char pathName[PATH_MAX]; | |
1628 | UInt32 ioPathLength = sizeof(pathName); | |
1629 | bzero(pathName, ioPathLength); | |
1630 | keychain = (SecKeychainRef)CFArrayGetValueAtIndex(dynamic, i); | |
1631 | result = SecKeychainGetPath(keychain, &ioPathLength, pathName); | |
1632 | if (result) | |
1633 | { | |
1634 | // sec_error("SecKeychainGetPath %s: %s", name, sec_errstr(result)); | |
1635 | cssmPerror("SecKeychainCopyDomainSearchList", result); | |
1636 | return NULL; | |
1637 | } | |
1638 | if (!strncmp(pathName, name, ioPathLength)) | |
1639 | { | |
1640 | CFRetain(keychain); | |
1641 | CFRelease(dynamic); | |
1642 | return keychain; | |
1643 | } | |
1644 | } | |
1645 | CFRelease(dynamic); | |
1646 | } | |
1647 | } | |
1648 | ||
1649 | result = SecKeychainOpen(name, &keychain); | |
1650 | if (result) | |
1651 | { | |
1652 | // sec_error("SecKeychainOpen %s: %s", name, sec_errstr(result)); | |
1653 | cssmPerror("SecKeychainOpen", result); | |
1654 | } | |
1655 | ||
1656 | return keychain; | |
1657 | } | |
1658 |