]> git.saurik.com Git - apple/security.git/blob - SecurityTool/cmsutil.c
Security-57740.51.3.tar.gz
[apple/security.git] / SecurityTool / cmsutil.c
1 /*
2 * The contents of this file are subject to the Mozilla Public
3 * License Version 1.1 (the "License"); you may not use this file
4 * except in compliance with the License. You may obtain a copy of
5 * the License at http://www.mozilla.org/MPL/
6 *
7 * Software distributed under the License is distributed on an "AS
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9 * implied. See the License for the specific language governing
10 * rights and limitations under the License.
11 *
12 * The Original Code is the Netscape security libraries.
13 *
14 * The Initial Developer of the Original Code is Netscape
15 * Communications Corporation. Portions created by Netscape are
16 * Copyright (C) 1994-2000 Netscape Communications Corporation. All
17 * Rights Reserved.
18 *
19 * Contributor(s):
20 *
21 * Alternatively, the contents of this file may be used under the
22 * terms of the GNU General Public License Version 2 or later (the
23 * "GPL"), in which case the provisions of the GPL are applicable
24 * instead of those above. If you wish to allow use of your
25 * version of this file only under the terms of the GPL and not to
26 * allow others to use your version of this file under the MPL,
27 * indicate your decision by deleting the provisions above and
28 * replace them with the notice and other provisions required by
29 * the GPL. If you do not delete the provisions above, a recipient
30 * may use your version of this file under either the MPL or the
31 * GPL.
32 */
33
34 /*
35 * Modifications Copyright (c) 2003-2009,2012,2014 Apple Inc. All Rights Reserved.
36 *
37 * cmsutil -- A command to work with CMS data
38 */
39
40 #include "security_tool.h"
41 #include "keychain_utilities.h"
42 #include "identity_find.h"
43
44 #include <Security/SecCmsBase.h>
45 #include <Security/SecCmsContentInfo.h>
46 #include <Security/SecCmsDecoder.h>
47 #include <Security/SecCmsDigestContext.h>
48 #include <Security/SecCmsDigestedData.h>
49 #include <Security/SecCmsEncoder.h>
50 #include <Security/SecCmsEncryptedData.h>
51 #include <Security/SecCmsEnvelopedData.h>
52 #include <Security/SecCmsMessage.h>
53 #include <Security/SecCmsRecipientInfo.h>
54 #include <Security/SecCmsSignedData.h>
55 #include <Security/SecCmsSignerInfo.h>
56 #include <Security/SecSMIME.h>
57 #include <Security/tsaSupport.h>
58
59 #include <Security/oidsalg.h>
60 #include <Security/SecPolicy.h>
61 #include <Security/SecKeychain.h>
62 #include <Security/SecKeychainSearch.h>
63 #include <Security/SecIdentity.h>
64 #include <Security/SecIdentitySearch.h>
65 #include <CoreFoundation/CFString.h>
66 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
67
68 #include <stdio.h>
69 #include <stdarg.h>
70 #include <stdio.h>
71 #include <string.h>
72 #include <unistd.h>
73
74 #include "cmsutil.h"
75
76 // SecPolicyCopy
77 #include <Security/SecPolicyPriv.h>
78 // SecKeychainSearchCreateForCertificateByEmail, SecCertificateFindBySubjectKeyID, SecCertificateFindByEmail
79 #include <Security/SecCertificatePriv.h>
80 // SecIdentitySearchCreateWithPolicy
81 #include <Security/SecIdentitySearchPriv.h>
82
83 #define SEC_CHECK0(CALL, ERROR) do { if (!(CALL)) { sec_error(ERROR); goto loser; } } while(0)
84 #define SEC_CHECK(CALL, ERROR) do { rv = (CALL); if (rv) { sec_perror(ERROR, rv); goto loser; } } while(0)
85 #define SEC_CHECK2(CALL, ERROR, ARG) do { rv = CALL; if (rv) \
86 { sec_error(ERROR ": %s", ARG, sec_errstr(rv)); goto loser; } } while(0)
87
88 // @@@ Remove this
89 #if 1
90
91 static CSSM_KEYUSE CERT_KeyUsageForCertUsage(SECCertUsage certUsage)
92 {
93 switch (certUsage)
94 {
95 case certUsageSSLClient: return CSSM_KEYUSE_SIGN;
96 case certUsageSSLServer: return CSSM_KEYUSE_SIGN;
97 case certUsageSSLServerWithStepUp: return CSSM_KEYUSE_SIGN;
98 case certUsageSSLCA: return CSSM_KEYUSE_SIGN;
99 case certUsageEmailSigner: return CSSM_KEYUSE_SIGN;
100 case certUsageEmailRecipient: return CSSM_KEYUSE_UNWRAP;
101 case certUsageObjectSigner: return CSSM_KEYUSE_SIGN;
102 case certUsageUserCertImport: return CSSM_KEYUSE_SIGN;
103 case certUsageVerifyCA: return CSSM_KEYUSE_SIGN;
104 case certUsageProtectedObjectSigner: return CSSM_KEYUSE_SIGN;
105 case certUsageStatusResponder: return CSSM_KEYUSE_SIGN;
106 case certUsageAnyCA: return CSSM_KEYUSE_SIGN;
107 default:
108 sec_error("CERT_PolicyForCertUsage %ld: unknown certUsage", certUsage);
109 return CSSM_KEYUSE_SIGN;
110 }
111 }
112
113 static SecPolicyRef CERT_PolicyForCertUsage(SECCertUsage certUsage, const char *emailAddress)
114 {
115 SecPolicyRef policy = NULL;
116 const CSSM_OID *policyOID;
117 OSStatus rv;
118
119 switch (certUsage)
120 {
121 case certUsageSSLClient: policyOID = &CSSMOID_APPLE_TP_SSL; break;
122 case certUsageSSLServer: policyOID = &CSSMOID_APPLE_TP_SSL; break;
123 case certUsageSSLServerWithStepUp: policyOID = &CSSMOID_APPLE_TP_SSL; break;
124 case certUsageSSLCA: policyOID = &CSSMOID_APPLE_TP_SSL; break;
125 case certUsageEmailSigner: policyOID = &CSSMOID_APPLE_TP_SMIME; break;
126 case certUsageEmailRecipient: policyOID = &CSSMOID_APPLE_TP_SMIME; break;
127 case certUsageObjectSigner: policyOID = &CSSMOID_APPLE_TP_CODE_SIGN; break;
128 case certUsageUserCertImport: policyOID = &CSSMOID_APPLE_X509_BASIC; break;
129 case certUsageVerifyCA: policyOID = &CSSMOID_APPLE_X509_BASIC; break;
130 case certUsageProtectedObjectSigner: policyOID = &CSSMOID_APPLE_ISIGN; break;
131 case certUsageStatusResponder: policyOID = &CSSMOID_APPLE_TP_REVOCATION_OCSP; break;
132 case certUsageAnyCA: policyOID = &CSSMOID_APPLE_X509_BASIC; break;
133 default:
134 sec_error("CERT_PolicyForCertUsage %ld: unknown certUsage", certUsage);
135 goto loser;
136 }
137
138 SEC_CHECK(SecPolicyCopy(CSSM_CERT_X_509v3, policyOID, &policy), "SecPolicyCopy");
139 if (certUsage == certUsageEmailSigner || certUsage == certUsageEmailRecipient)
140 {
141 CSSM_APPLE_TP_SMIME_OPTIONS options =
142 {
143 CSSM_APPLE_TP_SMIME_OPTS_VERSION,
144 certUsage == certUsageEmailSigner
145 ? CE_KU_DigitalSignature | CE_KU_NonRepudiation
146 : CE_KU_KeyEncipherment,
147 emailAddress ? sizeof(emailAddress) : 0,
148 emailAddress
149 };
150 CSSM_DATA value = { sizeof(options), (uint8 *)&options };
151 SEC_CHECK(SecPolicySetValue(policy, &value), "SecPolicySetValue");
152 }
153
154 // @@@ Need to set values for SSL and other policies.
155 return policy;
156
157 loser:
158 if (policy) CFRelease(policy);
159 return NULL;
160 }
161
162 static SecCertificateRef CERT_FindUserCertByUsage(CFTypeRef keychainOrArray, const char *emailAddress,
163 SECCertUsage certUsage, Boolean validOnly)
164 {
165 SecKeychainSearchRef search = NULL;
166 SecCertificateRef cert = NULL;
167 SecPolicyRef policy;
168 OSStatus rv;
169
170 policy = CERT_PolicyForCertUsage(certUsage, emailAddress);
171 if (!policy)
172 goto loser;
173
174 SEC_CHECK2(SecKeychainSearchCreateForCertificateByEmail(keychainOrArray, emailAddress, &search),
175 "create search for certificate with email: \"%s\"", emailAddress);
176 for (;;)
177 {
178 SecKeychainItemRef item;
179 rv = SecKeychainSearchCopyNext(search, &item);
180 if (rv)
181 {
182 if (rv == errSecItemNotFound)
183 break;
184
185 sec_perror("error finding next matching certificate", rv);
186 goto loser;
187 }
188
189 cert = (SecCertificateRef)item;
190 // @@@ Check cert against policy.
191 }
192
193 loser:
194 if (policy) CFRelease(policy);
195 if (search) CFRelease(search);
196
197 return cert;
198 }
199
200 static SecIdentityRef CERT_FindIdentityByUsage(CFTypeRef keychainOrArray,
201 const char *emailAddress,
202 SECCertUsage certUsage,
203 Boolean validOnly)
204 {
205 SecIdentitySearchRef search = NULL;
206 SecIdentityRef identity = NULL;
207 CFStringRef idString = CFStringCreateWithCString(NULL, emailAddress, kCFStringEncodingUTF8);
208 SecPolicyRef policy;
209 OSStatus rv;
210
211 policy = CERT_PolicyForCertUsage(certUsage, emailAddress);
212 if (!policy)
213 goto loser;
214
215
216 SEC_CHECK2(SecIdentitySearchCreateWithPolicy(policy, idString,
217 CERT_KeyUsageForCertUsage(certUsage), keychainOrArray, validOnly, &search),
218 "create search for identity with email: \"%s\"", emailAddress);
219 for (;;)
220 {
221 rv = SecIdentitySearchCopyNext(search, &identity);
222 if (rv)
223 {
224 if (rv == errSecItemNotFound)
225 break;
226
227 sec_perror("error finding next matching identity", rv);
228 goto loser;
229 }
230 }
231
232 loser:
233 if (policy) CFRelease(policy);
234 if (search) CFRelease(search);
235 if (idString) CFRelease(idString);
236
237 return identity;
238
239
240 #if 0
241 SecIdentityRef identity = NULL;
242 SecCertificateRef cert;
243 OSStatus rv;
244
245 cert = CERT_FindUserCertByUsage(keychainOrArray, emailAddress, certUsage, validOnly);
246 if (!cert)
247 goto loser;
248
249 SEC_CHECK2(SecIdentityCreateWithCertificate(keychainOrArray, cert, &identity),
250 "failed to find private key for certificate with email: \"%s\"", emailAddress);
251 loser:
252 if (cert) CFRelease(cert);
253
254 return identity;
255 #endif
256 }
257
258 static SecCertificateRef CERT_FindCertByNicknameOrEmailAddr(CFTypeRef keychainOrArray, const char *emailAddress)
259 {
260 SecCertificateRef certificate = NULL;
261 OSStatus rv;
262
263 SEC_CHECK2(SecCertificateFindByEmail(keychainOrArray, emailAddress, &certificate),
264 "failed to find certificate with email: \"%s\"", emailAddress);
265
266 loser:
267 return certificate;
268 }
269
270 static SecIdentityRef CERT_FindIdentityBySubjectKeyID(CFTypeRef keychainOrArray,
271 const char *subjectKeyIDString)
272 {
273 // ss will be something like "B2ACD31AC8D0DA62E7679432ADDD3398EF66948B"
274
275 SecCertificateRef certificate = NULL;
276 SecIdentityRef identityRef = NULL;
277 OSStatus rv;
278
279 CSSM_SIZE len = strlen(subjectKeyIDString)/2;
280 CSSM_DATA subjectKeyID = {0,};
281 subjectKeyID.Length = len;
282 subjectKeyID.Data = (uint8 *)malloc(subjectKeyID.Length);
283 fromHex(subjectKeyIDString, &subjectKeyID);
284
285 SEC_CHECK2(SecCertificateFindBySubjectKeyID(keychainOrArray, &subjectKeyID, &certificate),
286 "failed to find identity with subject key ID: \"%s\"", subjectKeyIDString);
287
288 SEC_CHECK2(SecIdentityCreateWithCertificate(keychainOrArray, certificate, &identityRef),
289 "failed to find certificate with subject key ID: \"%s\"", subjectKeyIDString);
290 loser:
291 return identityRef;
292 }
293
294
295 static OSStatus CERT_CheckCertUsage (SecCertificateRef cert,unsigned char usage)
296 {
297 return 0;
298 }
299
300 #endif
301
302 // @@@ Eleminate usage of this header.
303 //#include "cert.h"
304 //#include <security_asn1/secerr.h>
305 //#include "plgetopt.h"
306 //#include "secitem.h"
307
308 #ifdef HAVE_DODUMPSTATES
309 extern int doDumpStates;
310 #endif /* HAVE_DODUMPSTATES */
311
312 OSStatus SECU_FileToItem(CSSM_DATA *dst, FILE *src);
313
314
315 extern void SEC_Init(void); /* XXX */
316 static int cms_verbose = 0;
317 static int cms_update_single_byte = 0;
318
319 /* XXX stolen from cmsarray.c
320 * nss_CMSArray_Count - count number of elements in array
321 */
322 static int nss_CMSArray_Count(void **array)
323 {
324 int n = 0;
325 if (array == NULL)
326 return 0;
327 while (*array++ != NULL)
328 n++;
329 return n;
330 }
331
332 typedef OSStatus(update_func)(void *cx, const char *data, size_t len);
333
334 static OSStatus do_update(update_func *update,
335 void *cx, const unsigned char *data, size_t len)
336 {
337 OSStatus rv = noErr;
338 if (cms_update_single_byte)
339 {
340 for (;len; --len, ++data)
341 {
342 rv = update(cx, (const char *)data, 1);
343 if (rv)
344 break;
345 }
346 }
347 else
348 rv = update(cx, (const char *)data, len);
349
350 return rv;
351 }
352
353
354 static OSStatus DigestFile(SecArenaPoolRef poolp, CSSM_DATA ***digests, CSSM_DATA *input, SECAlgorithmID **algids)
355 {
356 SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(algids);
357 if (digcx == NULL)
358 return paramErr;
359
360 do_update((update_func *)SecCmsDigestContextUpdate, digcx, input->Data, input->Length);
361
362 return SecCmsDigestContextFinishMultiple(digcx, poolp, digests);
363 }
364
365 static char *
366 ownpw(void *info, Boolean retry, void *arg)
367 {
368 char * passwd = NULL;
369
370 if ( (!retry) && arg ) {
371 passwd = strdup((char *)arg);
372 }
373
374 return passwd;
375 }
376
377 struct optionsStr {
378 PK11PasswordFunc password;
379 SECCertUsage certUsage;
380 SecKeychainRef certDBHandle;
381 };
382
383 struct decodeOptionsStr {
384 struct optionsStr *options;
385 FILE *contentFile;
386 int headerLevel;
387 Boolean suppressContent;
388 SecCmsGetDecryptKeyCallback dkcb;
389 SecSymmetricKeyRef bulkkey;
390 };
391
392 struct signOptionsStr {
393 struct optionsStr *options;
394 char *nickname;
395 char *encryptionKeyPreferenceNick;
396 char *subjectKeyID;
397 Boolean signingTime;
398 Boolean smimeProfile;
399 Boolean detached;
400 SECOidTag hashAlgTag;
401 Boolean wantTimestamping;
402 char *timestampingURL;
403 };
404
405 struct envelopeOptionsStr {
406 struct optionsStr *options;
407 char **recipients;
408 };
409
410 struct certsonlyOptionsStr {
411 struct optionsStr *options;
412 char **recipients;
413 };
414
415 struct encryptOptionsStr {
416 struct optionsStr *options;
417 char **recipients;
418 SecCmsMessageRef envmsg;
419 CSSM_DATA *input;
420 FILE *outfile;
421 FILE *envFile;
422 SecSymmetricKeyRef bulkkey;
423 SECOidTag bulkalgtag;
424 int keysize;
425 };
426
427 static SecCmsMessageRef decode(FILE *out, CSSM_DATA *output, CSSM_DATA *input,
428 const struct decodeOptionsStr *decodeOptions)
429 {
430 SecCmsDecoderRef dcx;
431 SecCmsMessageRef cmsg=NULL;
432 SecCmsContentInfoRef cinfo;
433 SecCmsSignedDataRef sigd = NULL;
434 SecCmsEnvelopedDataRef envd;
435 SecCmsEncryptedDataRef encd;
436 SECAlgorithmID **digestalgs;
437 int nlevels, i, nsigners, j;
438 CFStringRef signercn;
439 SecCmsSignerInfoRef si;
440 SECOidTag typetag;
441 CSSM_DATA **digests;
442 SecArenaPoolRef poolp = NULL;
443 PK11PasswordFunc pwcb;
444 void *pwcb_arg;
445 CSSM_DATA *item, sitem = { 0, };
446 CFTypeRef policy = NULL;
447 OSStatus rv;
448
449 pwcb = (PK11PasswordFunc)((decodeOptions->options->password != NULL) ? ownpw : NULL);
450 pwcb_arg = (decodeOptions->options->password != NULL) ?
451 (void *)decodeOptions->options->password : NULL;
452
453 if (decodeOptions->contentFile) // detached content: grab content file
454 SECU_FileToItem(&sitem, decodeOptions->contentFile);
455
456 SEC_CHECK(SecCmsDecoderCreate(NULL,
457 NULL, NULL, /* content callback */
458 pwcb, pwcb_arg, /* password callback */
459 decodeOptions->dkcb, /* decrypt key callback */
460 decodeOptions->bulkkey,
461 &dcx),
462 "failed to create to decoder");
463 SEC_CHECK(do_update((update_func *)SecCmsDecoderUpdate, dcx, input->Data, input->Length),
464 "failed to add data to decoder");
465 SEC_CHECK(SecCmsDecoderFinish(dcx, &cmsg),
466 "failed to decode message");
467
468 if (decodeOptions->headerLevel >= 0)
469 {
470 /*fprintf(out, "SMIME: ", decodeOptions->headerLevel, i);*/
471 fprintf(out, "SMIME: ");
472 }
473
474 nlevels = SecCmsMessageContentLevelCount(cmsg);
475 for (i = 0; i < nlevels; i++)
476 {
477 cinfo = SecCmsMessageContentLevel(cmsg, i);
478 typetag = SecCmsContentInfoGetContentTypeTag(cinfo);
479
480 if (decodeOptions->headerLevel >= 0)
481 fprintf(out, "\tlevel=%d.%d; ", decodeOptions->headerLevel, nlevels - i);
482
483 switch (typetag)
484 {
485 case SEC_OID_PKCS7_SIGNED_DATA:
486 if (decodeOptions->headerLevel >= 0)
487 fprintf(out, "type=signedData; ");
488
489 SEC_CHECK0(sigd = (SecCmsSignedDataRef )SecCmsContentInfoGetContent(cinfo),
490 "problem finding signedData component");
491 /* if we have a content file, but no digests for this signedData */
492 if (decodeOptions->contentFile != NULL && !SecCmsSignedDataHasDigests(sigd))
493 {
494 SEC_CHECK(SecArenaPoolCreate(1024, &poolp), "failed to create arenapool");
495 digestalgs = SecCmsSignedDataGetDigestAlgs(sigd);
496 SEC_CHECK(DigestFile(poolp, &digests, &sitem, digestalgs),
497 "problem computing message digest");
498 SEC_CHECK(SecCmsSignedDataSetDigests(sigd, digestalgs, digests),
499 "problem setting message digests");
500 SecArenaPoolFree(poolp, false);
501 }
502
503 policy = CERT_PolicyForCertUsage(decodeOptions->options->certUsage, NULL);
504 // import the certificates
505 SEC_CHECK(SecCmsSignedDataImportCerts(sigd,decodeOptions->options->certDBHandle,
506 decodeOptions->options->certUsage, true /* false */),
507 "cert import failed");
508
509 /* find out about signers */
510 nsigners = SecCmsSignedDataSignerInfoCount(sigd);
511 if (decodeOptions->headerLevel >= 0)
512 fprintf(out, "nsigners=%d; ", nsigners);
513 if (nsigners == 0)
514 {
515 /* must be a cert transport message */
516 OSStatus rv;
517 /* XXX workaround for bug #54014 */
518 SecCmsSignedDataImportCerts(sigd,decodeOptions->options->certDBHandle,
519 decodeOptions->options->certUsage,true);
520 SEC_CHECK(SecCmsSignedDataVerifyCertsOnly(sigd,decodeOptions->options->certDBHandle, policy),
521 "verify certs-only failed");
522 return cmsg;
523 }
524
525 SEC_CHECK0(SecCmsSignedDataHasDigests(sigd), "message has no digests");
526 for (j = 0; j < nsigners; j++)
527 {
528 si = SecCmsSignedDataGetSignerInfo(sigd, j);
529 signercn = SecCmsSignerInfoGetSignerCommonName(si);
530 if (decodeOptions->headerLevel >= 0)
531 {
532 const char *px = signercn ? CFStringGetCStringPtr(signercn,kCFStringEncodingMacRoman) : "<NULL>";
533 fprintf(out, "\n\t\tsigner%d.id=\"%s\"; ", j, px);
534 }
535 SecCmsSignedDataVerifySignerInfo(sigd, j, decodeOptions->options->certDBHandle,
536 policy, NULL);
537 if (decodeOptions->headerLevel >= 0)
538 fprintf(out, "signer%d.status=%s; ", j,
539 SecCmsUtilVerificationStatusToString(SecCmsSignerInfoGetVerificationStatus(si)));
540 /* XXX what do we do if we don't print headers? */
541 }
542 break;
543 case SEC_OID_PKCS7_ENVELOPED_DATA:
544 if (decodeOptions->headerLevel >= 0)
545 fprintf(out, "type=envelopedData; ");
546 envd = (SecCmsEnvelopedDataRef )SecCmsContentInfoGetContent(cinfo);
547 break;
548 case SEC_OID_PKCS7_ENCRYPTED_DATA:
549 if (decodeOptions->headerLevel >= 0)
550 fprintf(out, "type=encryptedData; ");
551 encd = (SecCmsEncryptedDataRef )SecCmsContentInfoGetContent(cinfo);
552 break;
553 case SEC_OID_PKCS7_DATA:
554 if (decodeOptions->headerLevel >= 0)
555 fprintf(out, "type=data; ");
556 break;
557 default:
558 break;
559 }
560 if (decodeOptions->headerLevel >= 0)
561 fprintf(out, "\n");
562 }
563
564 if (!decodeOptions->suppressContent)
565 {
566 item = decodeOptions->contentFile ? &sitem :
567 SecCmsMessageGetContent(cmsg);
568 /* Copy the data. */
569 output->Length = item->Length;
570 output->Data = malloc(output->Length);
571 memcpy(output->Data, item->Data, output->Length);
572 }
573
574 if (policy) CFRelease(policy);
575
576 return cmsg;
577 loser:
578 if (policy) CFRelease(policy);
579 if (cmsg) SecCmsMessageDestroy(cmsg);
580 return NULL;
581 }
582
583 /* example of a callback function to use with encoder */
584 /*
585 static void
586 writeout(void *arg, const char *buf, unsigned long len)
587 {
588 FILE *f = (FILE *)arg;
589
590 if (f != NULL && buf != NULL)
591 (void)fwrite(buf, len, 1, f);
592 }
593 */
594
595 static SecCmsMessageRef signed_data(struct signOptionsStr *signOptions)
596 {
597 SecCmsMessageRef cmsg = NULL;
598 SecCmsContentInfoRef cinfo;
599 SecCmsSignedDataRef sigd;
600 SecCmsSignerInfoRef signerinfo;
601 SecIdentityRef identity = NULL;
602 SecCertificateRef cert = NULL, ekpcert = NULL;
603 OSStatus rv;
604
605 if (cms_verbose)
606 {
607 fprintf(stderr, "Input to signed_data:\n");
608 if (signOptions->options->password)
609 fprintf(stderr, "password [%s]\n", "***" /*signOptions->options->password*/);
610 else
611 fprintf(stderr, "password [NULL]\n");
612 fprintf(stderr, "certUsage [%d]\n", signOptions->options->certUsage);
613 if (signOptions->options->certDBHandle)
614 fprintf(stderr, "certdb [%p]\n", signOptions->options->certDBHandle);
615 else
616 fprintf(stderr, "certdb [NULL]\n");
617 if (signOptions->nickname)
618 fprintf(stderr, "nickname [%s]\n", signOptions->nickname);
619 else
620 fprintf(stderr, "nickname [NULL]\n");
621 if (signOptions->subjectKeyID)
622 fprintf(stderr, "subject Key ID [%s]\n", signOptions->subjectKeyID);
623 }
624
625 if (signOptions->subjectKeyID)
626 {
627 if ((identity = CERT_FindIdentityBySubjectKeyID(signOptions->options->certDBHandle,
628 signOptions->subjectKeyID)) == NULL)
629 {
630 sec_error("could not find signing identity for subject key ID: \"%s\"", signOptions->subjectKeyID);
631 return NULL;
632 }
633
634 if (cms_verbose)
635 fprintf(stderr, "Found identity for subject key ID %s\n", signOptions->subjectKeyID);
636 }
637 else if (signOptions->nickname)
638 {
639 if ((identity = CERT_FindIdentityByUsage(signOptions->options->certDBHandle,
640 signOptions->nickname,
641 signOptions->options->certUsage,
642 false)) == NULL)
643 {
644 // look for identity by common name rather than email address
645 if ((identity = find_identity(signOptions->options->certDBHandle,
646 signOptions->nickname,
647 NULL,
648 signOptions->options->certUsage)) == NULL)
649 {
650 sec_error("could not find signing identity for name: \"%s\"", signOptions->nickname);
651 return NULL;
652 }
653 }
654
655 if (cms_verbose)
656 fprintf(stderr, "Found identity for %s\n", signOptions->nickname);
657 }
658 else
659 {
660 // no identity was specified
661 sec_error("no signing identity was specified");
662 return NULL;
663 }
664
665 // Get the cert from the identity
666 SEC_CHECK(SecIdentityCopyCertificate(identity, &cert),
667 "SecIdentityCopyCertificate");
668 // create the message object on its own pool
669 SEC_CHECK0(cmsg = SecCmsMessageCreate(NULL), "cannot create CMS message");
670 // build chain of objects: message->signedData->data
671 SEC_CHECK0(sigd = SecCmsSignedDataCreate(cmsg),
672 "cannot create CMS signedData object");
673 SEC_CHECK0(cinfo = SecCmsMessageGetContentInfo(cmsg),
674 "message has no content info");
675 SEC_CHECK(SecCmsContentInfoSetContentSignedData(cmsg, cinfo, sigd),
676 "cannot attach CMS signedData object");
677 SEC_CHECK0(cinfo = SecCmsSignedDataGetContentInfo(sigd),
678 "signed data has no content info");
679 /* we're always passing data in and detaching optionally */
680 SEC_CHECK(SecCmsContentInfoSetContentData(cmsg, cinfo, NULL, signOptions->detached),
681 "cannot attach CMS data object");
682 // create & attach signer information
683 SEC_CHECK0(signerinfo = SecCmsSignerInfoCreate(cmsg, identity, signOptions->hashAlgTag),
684 "cannot create CMS signerInfo object");
685 if (cms_verbose)
686 fprintf(stderr,"Created CMS message, added signed data w/ signerinfo\n");
687
688 // we want the cert chain included for this one
689 SEC_CHECK(SecCmsSignerInfoIncludeCerts(signerinfo, SecCmsCMCertChain, signOptions->options->certUsage),
690 "cannot add cert chain");
691
692 if (cms_verbose)
693 fprintf(stderr, "imported certificate\n");
694
695 if (signOptions->signingTime)
696 SEC_CHECK(SecCmsSignerInfoAddSigningTime(signerinfo, CFAbsoluteTimeGetCurrent()),
697 "cannot add signingTime attribute");
698
699 if (signOptions->smimeProfile)
700 SEC_CHECK(SecCmsSignerInfoAddSMIMECaps(signerinfo),
701 "cannot add SMIMECaps attribute");
702
703 if (signOptions->wantTimestamping)
704 {
705 CFErrorRef error = NULL;
706 SecCmsMessageSetTSAContext(cmsg, SecCmsTSAGetDefaultContext(&error));
707 }
708
709 if (!signOptions->encryptionKeyPreferenceNick)
710 {
711 /* check signing cert for fitness as encryption cert */
712 OSStatus FitForEncrypt = CERT_CheckCertUsage(cert, certUsageEmailRecipient);
713
714 if (noErr == FitForEncrypt)
715 {
716 /* if yes, add signing cert as EncryptionKeyPreference */
717 SEC_CHECK(SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerinfo, cert, signOptions->options->certDBHandle),
718 "cannot add default SMIMEEncKeyPrefs attribute");
719 SEC_CHECK(SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerinfo, cert, signOptions->options->certDBHandle),
720 "cannot add default MS SMIMEEncKeyPrefs attribute");
721 }
722 else
723 {
724 /* this is a dual-key cert case, we need to look for the encryption
725 certificate under the same nickname as the signing cert */
726 /* get the cert, add it to the message */
727 if ((ekpcert = CERT_FindUserCertByUsage(
728 signOptions->options->certDBHandle,
729 signOptions->nickname,
730 certUsageEmailRecipient,
731 false)) == NULL)
732 {
733 sec_error("can find encryption cert for \"%s\"", signOptions->nickname);
734 goto loser;
735 }
736
737 SEC_CHECK(SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerinfo, ekpcert,signOptions->options->certDBHandle),
738 "cannot add SMIMEEncKeyPrefs attribute");
739 SEC_CHECK(SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerinfo, ekpcert,signOptions->options->certDBHandle),
740 "cannot add MS SMIMEEncKeyPrefs attribute");
741 SEC_CHECK(SecCmsSignedDataAddCertificate(sigd, ekpcert),
742 "cannot add encryption certificate");
743 }
744 }
745 else if (strcmp(signOptions->encryptionKeyPreferenceNick, "NONE") == 0)
746 {
747 /* No action */
748 }
749 else
750 {
751 /* specific email address for encryption preferred encryption cert specified.
752 get the cert, add it to the message */
753 if ((ekpcert = CERT_FindUserCertByUsage(
754 signOptions->options->certDBHandle,
755 signOptions->encryptionKeyPreferenceNick,
756 certUsageEmailRecipient, false)) == NULL)
757 {
758 sec_error("can find encryption cert for \"%s\"", signOptions->encryptionKeyPreferenceNick);
759 goto loser;
760 }
761
762 SEC_CHECK(SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerinfo, ekpcert,signOptions->options->certDBHandle),
763 "cannot add SMIMEEncKeyPrefs attribute");
764 SEC_CHECK(SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerinfo, ekpcert,signOptions->options->certDBHandle),
765 "cannot add MS SMIMEEncKeyPrefs attribute");
766 SEC_CHECK(SecCmsSignedDataAddCertificate(sigd, ekpcert),
767 "cannot add encryption certificate");
768 }
769
770 SEC_CHECK(SecCmsSignedDataAddSignerInfo(sigd, signerinfo),
771 "cannot add CMS signerInfo object");
772
773 if (cms_verbose)
774 fprintf(stderr, "created signed-data message\n");
775
776 if (ekpcert) CFRelease(ekpcert);
777 if (cert) CFRelease(cert);
778 if (identity) CFRelease(identity);
779 return cmsg;
780
781 loser:
782 if (ekpcert) CFRelease(ekpcert);
783 if (cert) CFRelease(cert);
784 if (identity) CFRelease(identity);
785 SecCmsMessageDestroy(cmsg);
786 return NULL;
787 }
788
789 static SecCmsMessageRef enveloped_data(struct envelopeOptionsStr *envelopeOptions)
790 {
791 SecCmsMessageRef cmsg = NULL;
792 SecCmsContentInfoRef cinfo;
793 SecCmsEnvelopedDataRef envd;
794 SecCmsRecipientInfoRef recipientinfo;
795 SecCertificateRef *recipientcerts = NULL;
796 SecKeychainRef dbhandle;
797 SECOidTag bulkalgtag;
798 OSStatus rv;
799 int keysize, i = 0;
800 int cnt;
801
802 dbhandle = envelopeOptions->options->certDBHandle;
803 /* count the recipients */
804 SEC_CHECK0(cnt = nss_CMSArray_Count((void **)envelopeOptions->recipients),
805 "please name at least one recipient");
806
807 // @@@ find the recipient's certs by email address or nickname
808 if ((recipientcerts = (SecCertificateRef *)calloc((cnt+1), sizeof(SecCertificateRef))) == NULL)
809 {
810 sec_error("failed to alloc certs array: %s", strerror(errno));
811 goto loser;
812 }
813
814 for (i = 0; envelopeOptions->recipients[i] != NULL; ++i)
815 {
816 if ((recipientcerts[i] =
817 CERT_FindCertByNicknameOrEmailAddr(dbhandle, envelopeOptions->recipients[i])) == NULL)
818 {
819 i = 0;
820 goto loser;
821 }
822 }
823
824 recipientcerts[i] = NULL;
825 i = 0;
826
827 // find a nice bulk algorithm
828 SEC_CHECK(SecSMIMEFindBulkAlgForRecipients(recipientcerts, &bulkalgtag, &keysize),
829 "cannot find common bulk algorithm");
830 // create the message object on its own pool
831 SEC_CHECK0(cmsg = SecCmsMessageCreate(NULL), "cannot create CMS message");
832 // build chain of objects: message->envelopedData->data
833 SEC_CHECK0(envd = SecCmsEnvelopedDataCreate(cmsg, bulkalgtag, keysize),
834 "cannot create CMS envelopedData object");
835 SEC_CHECK0(cinfo = SecCmsMessageGetContentInfo(cmsg),
836 "message has no content info");
837 SEC_CHECK(SecCmsContentInfoSetContentEnvelopedData(cmsg, cinfo, envd),
838 "cannot attach CMS envelopedData object");
839 SEC_CHECK0(cinfo = SecCmsEnvelopedDataGetContentInfo(envd),
840 "enveloped data has no content info");
841 // We're always passing data in, so the content is NULL
842 SEC_CHECK(SecCmsContentInfoSetContentData(cmsg, cinfo, NULL, false),
843 "cannot attach CMS data object");
844
845 // create & attach recipient information
846 for (i = 0; recipientcerts[i] != NULL; i++)
847 {
848 SEC_CHECK0(recipientinfo = SecCmsRecipientInfoCreate(cmsg, recipientcerts[i]),
849 "cannot create CMS recipientInfo object");
850 SEC_CHECK(SecCmsEnvelopedDataAddRecipient(envd, recipientinfo),
851 "cannot add CMS recipientInfo object");
852 CFRelease(recipientcerts[i]);
853 }
854
855 if (recipientcerts)
856 free(recipientcerts);
857
858 return cmsg;
859 loser:
860 if (recipientcerts)
861 {
862 for (; recipientcerts[i] != NULL; i++)
863 CFRelease(recipientcerts[i]);
864 }
865
866 if (cmsg)
867 SecCmsMessageDestroy(cmsg);
868
869 if (recipientcerts)
870 free(recipientcerts);
871
872 return NULL;
873 }
874
875 static SecSymmetricKeyRef dkcb(void *arg, SECAlgorithmID *algid)
876 {
877 return (SecSymmetricKeyRef)arg;
878 }
879
880 static OSStatus get_enc_params(struct encryptOptionsStr *encryptOptions)
881 {
882 struct envelopeOptionsStr envelopeOptions;
883 OSStatus rv = paramErr;
884 SecCmsMessageRef env_cmsg = NULL;
885 SecCmsContentInfoRef cinfo;
886 int i, nlevels;
887
888 // construct an enveloped data message to obtain bulk keys
889 if (encryptOptions->envmsg)
890 env_cmsg = encryptOptions->envmsg; // get it from an old message
891 else
892 {
893 CSSM_DATA dummyOut = { 0, };
894 CSSM_DATA dummyIn = { 0, };
895 char str[] = "Hello!";
896 SecArenaPoolRef tmparena = NULL;
897
898 SEC_CHECK(SecArenaPoolCreate(1024, &tmparena), "failed to create arenapool");
899
900 dummyIn.Data = (unsigned char *)str;
901 dummyIn.Length = strlen(str);
902 envelopeOptions.options = encryptOptions->options;
903 envelopeOptions.recipients = encryptOptions->recipients;
904 env_cmsg = enveloped_data(&envelopeOptions);
905 rv = SecCmsMessageEncode(env_cmsg, &dummyIn, tmparena, &dummyOut);
906 if (rv) {
907 goto loser;
908 }
909 fwrite(dummyOut.Data, 1, dummyOut.Length,encryptOptions->envFile);
910
911 SecArenaPoolFree(tmparena, false);
912 }
913
914 // get the content info for the enveloped data
915 nlevels = SecCmsMessageContentLevelCount(env_cmsg);
916 for (i = 0; i < nlevels; i++)
917 {
918 SECOidTag typetag;
919 cinfo = SecCmsMessageContentLevel(env_cmsg, i);
920 typetag = SecCmsContentInfoGetContentTypeTag(cinfo);
921 if (typetag == SEC_OID_PKCS7_DATA)
922 {
923 // get the symmetric key
924 encryptOptions->bulkalgtag = SecCmsContentInfoGetContentEncAlgTag(cinfo);
925 encryptOptions->keysize = SecCmsContentInfoGetBulkKeySize(cinfo);
926 encryptOptions->bulkkey = SecCmsContentInfoGetBulkKey(cinfo);
927 rv = noErr;
928 break;
929 }
930 }
931 if (i == nlevels)
932 sec_error("could not retrieve enveloped data: messsage has: %ld levels", nlevels);
933
934 loser:
935 if (env_cmsg)
936 SecCmsMessageDestroy(env_cmsg);
937
938 return rv;
939 }
940
941 static SecCmsMessageRef encrypted_data(struct encryptOptionsStr *encryptOptions)
942 {
943 OSStatus rv = paramErr;
944 SecCmsMessageRef cmsg = NULL;
945 SecCmsContentInfoRef cinfo;
946 SecCmsEncryptedDataRef encd;
947 SecCmsEncoderRef ecx = NULL;
948 SecArenaPoolRef tmppoolp = NULL;
949 CSSM_DATA derOut = { 0, };
950
951 /* arena for output */
952 SEC_CHECK(SecArenaPoolCreate(1024, &tmppoolp), "failed to create arenapool");
953 // create the message object on its own pool
954 SEC_CHECK0(cmsg = SecCmsMessageCreate(NULL), "cannot create CMS message");
955 // build chain of objects: message->encryptedData->data
956 SEC_CHECK0(encd = SecCmsEncryptedDataCreate(cmsg, encryptOptions->bulkalgtag,
957 encryptOptions->keysize),
958 "cannot create CMS encryptedData object");
959 SEC_CHECK0(cinfo = SecCmsMessageGetContentInfo(cmsg),
960 "message has no content info");
961 SEC_CHECK(SecCmsContentInfoSetContentEncryptedData(cmsg, cinfo, encd),
962 "cannot attach CMS encryptedData object");
963 SEC_CHECK0(cinfo = SecCmsEncryptedDataGetContentInfo(encd),
964 "encrypted data has no content info");
965 /* we're always passing data in, so the content is NULL */
966 SEC_CHECK(SecCmsContentInfoSetContentData(cmsg, cinfo, NULL, false),
967 "cannot attach CMS data object");
968 SEC_CHECK(SecCmsEncoderCreate(cmsg, NULL, NULL, &derOut, tmppoolp, NULL, NULL,
969 dkcb, encryptOptions->bulkkey, NULL, NULL, &ecx),
970 "cannot create encoder context");
971 SEC_CHECK(do_update((update_func *)SecCmsEncoderUpdate, ecx, encryptOptions->input->Data,
972 encryptOptions->input->Length),
973 "failed to add data to encoder");
974 SEC_CHECK(SecCmsEncoderFinish(ecx), "failed to encrypt data");
975 fwrite(derOut.Data, derOut.Length, 1, encryptOptions->outfile);
976 /* @@@ Check and report write errors. */
977 /*
978 if (bulkkey)
979 CFRelease(bulkkey);
980 */
981
982 if (tmppoolp)
983 SecArenaPoolFree(tmppoolp, false);
984 return cmsg;
985 loser:
986 /*
987 if (bulkkey)
988 CFRelease(bulkkey);
989 */
990 if (tmppoolp)
991 SecArenaPoolFree(tmppoolp, false);
992 if (cmsg)
993 SecCmsMessageDestroy(cmsg);
994
995 return NULL;
996 }
997
998 static SecCmsMessageRef signed_data_certsonly(struct certsonlyOptionsStr *certsonlyOptions)
999 {
1000 SecCmsMessageRef cmsg = NULL;
1001 SecCmsContentInfoRef cinfo;
1002 SecCmsSignedDataRef sigd;
1003 SecCertificateRef *certs = NULL;
1004 SecKeychainRef dbhandle;
1005 OSStatus rv;
1006
1007 int i = 0, cnt;
1008 dbhandle = certsonlyOptions->options->certDBHandle;
1009 SEC_CHECK0(cnt = nss_CMSArray_Count((void**)certsonlyOptions->recipients),
1010 "please indicate the nickname of a certificate to sign with");
1011 if ((certs = (SecCertificateRef *)calloc((cnt+1), sizeof(SecCertificateRef))) == NULL)
1012 {
1013 sec_error("failed to alloc certs array: %s", strerror(errno));
1014 goto loser;
1015 }
1016 for (i=0; certsonlyOptions->recipients && certsonlyOptions->recipients[i] != NULL; i++)
1017 {
1018 if ((certs[i] = CERT_FindCertByNicknameOrEmailAddr(dbhandle,certsonlyOptions->recipients[i])) == NULL)
1019 {
1020 i=0;
1021 goto loser;
1022 }
1023 }
1024 certs[i] = NULL;
1025 i = 0;
1026 // create the message object on its own pool
1027 SEC_CHECK0(cmsg = SecCmsMessageCreate(NULL), "cannot create CMS message");
1028 // build chain of objects: message->signedData->data
1029 SEC_CHECK0(sigd = SecCmsSignedDataCreateCertsOnly(cmsg, certs[0], true),
1030 "cannot create certs only CMS signedData object");
1031 CFRelease(certs[0]);
1032 for (i = 1; i < cnt; ++i)
1033 {
1034 SEC_CHECK2(SecCmsSignedDataAddCertChain(sigd, certs[i]),
1035 "cannot add cert chain for \"%s\"", certsonlyOptions->recipients[i]);
1036 CFRelease(certs[i]);
1037 }
1038
1039 SEC_CHECK0(cinfo = SecCmsMessageGetContentInfo(cmsg),
1040 "message has no content info");
1041 SEC_CHECK(SecCmsContentInfoSetContentSignedData(cmsg, cinfo, sigd),
1042 "cannot attach CMS signedData object");
1043 SEC_CHECK0(cinfo = SecCmsSignedDataGetContentInfo(sigd),
1044 "signed data has no content info");
1045 SEC_CHECK(SecCmsContentInfoSetContentData(cmsg, cinfo, NULL, false),
1046 "cannot attach CMS data object");
1047
1048 if (certs)
1049 free(certs);
1050
1051 return cmsg;
1052 loser:
1053 if (certs)
1054 {
1055 for (; i < cnt; ++i)
1056 CFRelease(certs[i]);
1057
1058 free(certs);
1059 }
1060 if (cmsg) SecCmsMessageDestroy(cmsg);
1061
1062 return NULL;
1063 }
1064
1065 typedef enum { UNKNOWN, DECODE, SIGN, ENCRYPT, ENVELOPE, CERTSONLY } Mode;
1066
1067 int cms_util(int argc, char * const *argv)
1068 {
1069 FILE *outFile;
1070 SecCmsMessageRef cmsg = NULL;
1071 FILE *inFile;
1072 int ch;
1073 Mode mode = UNKNOWN;
1074 PK11PasswordFunc pwcb;
1075 void *pwcb_arg;
1076 struct decodeOptionsStr decodeOptions = { 0 };
1077 struct signOptionsStr signOptions = { 0 };
1078 struct envelopeOptionsStr envelopeOptions = { 0 };
1079 struct certsonlyOptionsStr certsonlyOptions = { 0 };
1080 struct encryptOptionsStr encryptOptions = { 0 };
1081 struct optionsStr options = { 0 };
1082 int result = 1;
1083 static char *ptrarray[128] = { 0 };
1084 int nrecipients = 0;
1085 char *str, *tok;
1086 char *envFileName;
1087 const char *keychainName = NULL;
1088 CSSM_DATA input = { 0,};
1089 CSSM_DATA output = { 0,};
1090 CSSM_DATA dummy = { 0, };
1091 CSSM_DATA envmsg = { 0, };
1092 OSStatus rv;
1093
1094 inFile = stdin;
1095 outFile = stdout;
1096 envFileName = NULL;
1097 mode = UNKNOWN;
1098 decodeOptions.contentFile = NULL;
1099 decodeOptions.suppressContent = false;
1100 decodeOptions.headerLevel = -1;
1101 options.certUsage = certUsageEmailSigner;
1102 options.password = NULL;
1103 signOptions.nickname = NULL;
1104 signOptions.subjectKeyID = NULL;
1105 signOptions.detached = false;
1106 signOptions.signingTime = false;
1107 signOptions.smimeProfile = false;
1108 signOptions.encryptionKeyPreferenceNick = NULL;
1109 signOptions.hashAlgTag = SEC_OID_SHA1;
1110 envelopeOptions.recipients = NULL;
1111 encryptOptions.recipients = NULL;
1112 encryptOptions.envmsg = NULL;
1113 encryptOptions.envFile = NULL;
1114 encryptOptions.bulkalgtag = SEC_OID_UNKNOWN;
1115 encryptOptions.bulkkey = NULL;
1116 encryptOptions.keysize = -1;
1117
1118 // Parse command line arguments
1119 while ((ch = getopt(argc, argv, "CDEGH:N:OPSTY:Z:c:de:h:i:k:no:p:r:su:vt:")) != -1)
1120 {
1121 switch (ch)
1122 {
1123 case 'C':
1124 mode = ENCRYPT;
1125 break;
1126 case 'D':
1127 mode = DECODE;
1128 break;
1129 case 'E':
1130 mode = ENVELOPE;
1131 break;
1132 case 'G':
1133 if (mode != SIGN) {
1134 sec_error("option -G only supported with option -S");
1135 result = 2; /* Trigger usage message. */
1136 goto loser;
1137 }
1138 signOptions.signingTime = true;
1139 break;
1140 case 'H':
1141 if (mode != SIGN) {
1142 sec_error("option -n only supported with option -D");
1143 result = 2; /* Trigger usage message. */
1144 goto loser;
1145 }
1146 decodeOptions.suppressContent = true;
1147 if (!strcmp(optarg, "MD2"))
1148 signOptions.hashAlgTag = SEC_OID_MD2;
1149 else if (!strcmp(optarg, "MD4"))
1150 signOptions.hashAlgTag = SEC_OID_MD4;
1151 else if (!strcmp(optarg, "MD5"))
1152 signOptions.hashAlgTag = SEC_OID_MD5;
1153 else if (!strcmp(optarg, "SHA1"))
1154 signOptions.hashAlgTag = SEC_OID_SHA1;
1155 else if (!strcmp(optarg, "SHA256"))
1156 signOptions.hashAlgTag = SEC_OID_SHA256;
1157 else if (!strcmp(optarg, "SHA384"))
1158 signOptions.hashAlgTag = SEC_OID_SHA384;
1159 else if (!strcmp(optarg, "SHA512"))
1160 signOptions.hashAlgTag = SEC_OID_SHA512;
1161 else {
1162 sec_error("option -H requires one of MD2,MD4,MD5,SHA1,SHA256,SHA384,SHA512");
1163 goto loser;
1164 }
1165 break;
1166 case 'N':
1167 if (mode != SIGN) {
1168 sec_error("option -N only supported with option -S");
1169 result = 2; /* Trigger usage message. */
1170 goto loser;
1171 }
1172 signOptions.nickname = strdup(optarg);
1173 break;
1174 case 'O':
1175 mode = CERTSONLY;
1176 break;
1177 case 'P':
1178 if (mode != SIGN) {
1179 sec_error("option -P only supported with option -S");
1180 result = 2; /* Trigger usage message. */
1181 goto loser;
1182 }
1183 signOptions.smimeProfile = true;
1184 break;
1185 case 'S':
1186 mode = SIGN;
1187 break;
1188 case 'T':
1189 if (mode != SIGN) {
1190 sec_error("option -T only supported with option -S");
1191 result = 2; /* Trigger usage message. */
1192 goto loser;
1193 }
1194 signOptions.detached = true;
1195 break;
1196 case 'Y':
1197 if (mode != SIGN) {
1198 sec_error("option -Y only supported with option -S");
1199 result = 2; /* Trigger usage message. */
1200 goto loser;
1201 }
1202 signOptions.encryptionKeyPreferenceNick = strdup(optarg);
1203 break;
1204
1205 case 'c':
1206 if (mode != DECODE)
1207 {
1208 sec_error("option -c only supported with option -D");
1209 result = 2; /* Trigger usage message. */
1210 goto loser;
1211 }
1212 if ((decodeOptions.contentFile = fopen(optarg, "rb")) == NULL)
1213 {
1214 sec_error("unable to open \"%s\" for reading: %s", optarg, strerror(errno));
1215 result = 1;
1216 goto loser;
1217 }
1218 break;
1219
1220 #ifdef HAVE_DODUMPSTATES
1221 case 'd':
1222 doDumpStates++;
1223 break;
1224 #endif /* HAVE_DODUMPSTATES */
1225
1226 case 'e':
1227 envFileName = strdup(optarg);
1228 encryptOptions.envFile = fopen(envFileName, "rb"); // PR_RDONLY, 00660);
1229 break;
1230
1231 case 'h':
1232 if (mode != DECODE) {
1233 sec_error("option -h only supported with option -D");
1234 result = 2; /* Trigger usage message. */
1235 goto loser;
1236 }
1237 decodeOptions.headerLevel = atoi(optarg);
1238 if (decodeOptions.headerLevel < 0) {
1239 sec_error("option -h cannot have a negative value");
1240 goto loser;
1241 }
1242 break;
1243 case 'i':
1244 inFile = fopen(optarg,"rb"); // PR_RDONLY, 00660);
1245 if (inFile == NULL)
1246 {
1247 sec_error("unable to open \"%s\" for reading: %s", optarg, strerror(errno));
1248 goto loser;
1249 }
1250 break;
1251
1252 case 'k':
1253 keychainName = optarg;
1254 break;
1255
1256 case 'n':
1257 if (mode != DECODE)
1258 {
1259 sec_error("option -n only supported with option -D");
1260 result = 2; /* Trigger usage message. */
1261 goto loser;
1262 }
1263 decodeOptions.suppressContent = true;
1264 break;
1265 case 'o':
1266 outFile = fopen(optarg, "wb");
1267 if (outFile == NULL)
1268 {
1269 sec_error("unable to open \"%s\" for writing: %s", optarg, strerror(errno));
1270 goto loser;
1271 }
1272 break;
1273 case 'p':
1274 if (!optarg)
1275 {
1276 sec_error("option -p must have a value");
1277 result = 2; /* Trigger usage message. */
1278 goto loser;
1279 }
1280
1281 options.password = (PK11PasswordFunc)ownpw;//strdup(optarg);
1282 break;
1283
1284 case 'r':
1285 if (!optarg)
1286 {
1287 sec_error("option -r must have a value");
1288 result = 2; /* Trigger usage message. */
1289 goto loser;
1290 }
1291
1292 envelopeOptions.recipients = ptrarray;
1293 str = (char *)optarg;
1294 do {
1295 tok = strchr(str, ',');
1296 if (tok) *tok = '\0';
1297 envelopeOptions.recipients[nrecipients++] = strdup(str);
1298 if (tok) str = tok + 1;
1299 } while (tok);
1300 envelopeOptions.recipients[nrecipients] = NULL;
1301 encryptOptions.recipients = envelopeOptions.recipients;
1302 certsonlyOptions.recipients = envelopeOptions.recipients;
1303 break;
1304
1305 case 's':
1306 cms_update_single_byte = 1;
1307 break;
1308
1309 case 'Z':
1310 if (!optarg)
1311 {
1312 sec_error("option -Z must have a value");
1313 result = 2; /* Trigger usage message. */
1314 goto loser;
1315 }
1316 signOptions.subjectKeyID = strdup(optarg);
1317
1318 break;
1319
1320 case 'u':
1321 {
1322 int usageType = atoi (strdup(optarg));
1323 if (usageType < certUsageSSLClient || usageType > certUsageAnyCA)
1324 {
1325 result = 1;
1326 goto loser;
1327 }
1328 options.certUsage = (SECCertUsage)usageType;
1329 break;
1330 }
1331 case 'v':
1332 cms_verbose = 1;
1333 break;
1334 case 't':
1335 if (optarg)
1336 signOptions.timestampingURL = strdup(optarg);
1337 signOptions.wantTimestamping = true;
1338 break;
1339 default:
1340 result = 2; /* Trigger usage message. */
1341 goto loser;
1342 }
1343 }
1344
1345 argc -= optind;
1346 argv += optind;
1347
1348 if (argc != 0 || mode == UNKNOWN)
1349 {
1350 result = 2; /* Trigger usage message. */
1351 goto loser;
1352 }
1353
1354 result = 0;
1355
1356 if (mode != CERTSONLY)
1357 SECU_FileToItem(&input, inFile);
1358 if (inFile != stdin)
1359 fclose(inFile);
1360 if (cms_verbose)
1361 fprintf(stderr, "received commands\n");
1362
1363 /* Call the libsec initialization routines */
1364 if (keychainName)
1365 {
1366 check_obsolete_keychain(keychainName);
1367 options.certDBHandle = keychain_open(keychainName);
1368 if (!options.certDBHandle)
1369 {
1370 sec_perror("SecKeychainOpen", errSecInvalidKeychain);
1371 result = 1;
1372 goto loser;
1373 }
1374 }
1375
1376 if (cms_verbose)
1377 fprintf(stderr, "Got default certdb\n");
1378
1379 switch (mode)
1380 {
1381 case DECODE:
1382 decodeOptions.options = &options;
1383 if (encryptOptions.envFile)
1384 {
1385 /* Decoding encrypted-data, so get the bulkkey from an
1386 * enveloped-data message.
1387 */
1388 SECU_FileToItem(&envmsg, encryptOptions.envFile);
1389 decodeOptions.options = &options;
1390 encryptOptions.envmsg = decode(NULL, &dummy, &envmsg, &decodeOptions);
1391 if (!encryptOptions.envmsg)
1392 {
1393 sec_error("problem decoding env msg");
1394 result = 1;
1395 break;
1396 }
1397 rv = get_enc_params(&encryptOptions);
1398 decodeOptions.dkcb = dkcb;
1399 decodeOptions.bulkkey = encryptOptions.bulkkey;
1400 }
1401 cmsg = decode(outFile, &output, &input, &decodeOptions);
1402 if (!cmsg)
1403 {
1404 sec_error("problem decoding");
1405 result = 1;
1406 }
1407 fwrite(output.Data, output.Length, 1, outFile);
1408 break;
1409 case SIGN:
1410 signOptions.options = &options;
1411 cmsg = signed_data(&signOptions);
1412 if (!cmsg)
1413 {
1414 sec_error("problem signing");
1415 result = 1;
1416 }
1417 break;
1418 case ENCRYPT:
1419 if (!envFileName)
1420 {
1421 sec_error("you must specify an envelope file with -e");
1422 result = 1;
1423 goto loser;
1424 }
1425 encryptOptions.options = &options;
1426 encryptOptions.input = &input;
1427 encryptOptions.outfile = outFile;
1428 if (!encryptOptions.envFile) {
1429 encryptOptions.envFile = fopen(envFileName,"wb"); //PR_WRONLY|PR_CREATE_FILE, 00660);
1430 if (!encryptOptions.envFile)
1431 {
1432 sec_error("failed to create file %s: %s", envFileName, strerror(errno));
1433 result = 1;
1434 goto loser;
1435 }
1436 }
1437 else
1438 {
1439 SECU_FileToItem(&envmsg, encryptOptions.envFile);
1440 decodeOptions.options = &options;
1441 encryptOptions.envmsg = decode(NULL, &dummy, &envmsg,
1442 &decodeOptions);
1443 if (encryptOptions.envmsg == NULL)
1444 {
1445 sec_error("problem decrypting env msg");
1446 result = 1;
1447 break;
1448 }
1449 }
1450
1451 /* decode an enveloped-data message to get the bulkkey (create
1452 * a new one if neccessary)
1453 */
1454 rv = get_enc_params(&encryptOptions);
1455 /* create the encrypted-data message */
1456 cmsg = encrypted_data(&encryptOptions);
1457 if (!cmsg)
1458 {
1459 sec_error("problem encrypting");
1460 result = 1;
1461 }
1462
1463 if (encryptOptions.bulkkey)
1464 {
1465 CFRelease(encryptOptions.bulkkey);
1466 encryptOptions.bulkkey = NULL;
1467 }
1468 break;
1469 case ENVELOPE:
1470 envelopeOptions.options = &options;
1471 cmsg = enveloped_data(&envelopeOptions);
1472 if (!cmsg)
1473 {
1474 sec_error("problem enveloping");
1475 result = 1;
1476 }
1477 break;
1478 case CERTSONLY:
1479 certsonlyOptions.options = &options;
1480 cmsg = signed_data_certsonly(&certsonlyOptions);
1481 if (!cmsg)
1482 {
1483 sec_error("problem with certs-only");
1484 result = 1;
1485 }
1486 break;
1487 case UNKNOWN:
1488 /* Already handled above. */
1489 break;
1490 }
1491
1492 if ( (mode == SIGN || mode == ENVELOPE || mode == CERTSONLY)
1493 && (!result) )
1494 {
1495 SecArenaPoolRef arena = NULL;
1496 SecCmsEncoderRef ecx;
1497 CSSM_DATA output = {};
1498
1499 SEC_CHECK(SecArenaPoolCreate(1024, &arena), "failed to create arenapool");
1500 pwcb = (PK11PasswordFunc)((options.password != NULL) ? ownpw : NULL);
1501 pwcb_arg = (options.password != NULL) ? (void *)options.password : NULL;
1502 if (cms_verbose) {
1503 fprintf(stderr, "cmsg [%p]\n", cmsg);
1504 fprintf(stderr, "arena [%p]\n", arena);
1505 if (pwcb_arg)
1506 fprintf(stderr, "password [%s]\n", (char *)pwcb_arg);
1507 else
1508 fprintf(stderr, "password [NULL]\n");
1509 }
1510
1511 SEC_CHECK(SecCmsEncoderCreate(cmsg,
1512 NULL, NULL, /* DER output callback */
1513 &output, arena, /* destination storage */
1514 pwcb, pwcb_arg, /* password callback */
1515 NULL, NULL, /* decrypt key callback */
1516 NULL, NULL, /* detached digests */
1517 &ecx),
1518 "cannot create encoder context");
1519 if (cms_verbose)
1520 {
1521 fprintf(stderr, "input len [%ld]\n", input.Length);
1522 {
1523 unsigned int j;
1524 for (j = 0; j < input.Length; ++j)
1525 fprintf(stderr, "%2x%c", input.Data[j], (j>0&&j%35==0)?'\n':' ');
1526 }
1527 }
1528
1529 if (input.Length > 0) { /* skip if certs-only (or other zero content) */
1530 SEC_CHECK(SecCmsEncoderUpdate(ecx, (char *)input.Data, input.Length),
1531 "failed to add data to encoder");
1532 }
1533
1534 SEC_CHECK(SecCmsEncoderFinish(ecx), "failed to encode data");
1535
1536 if (cms_verbose) {
1537 fprintf(stderr, "encoding passed\n");
1538 }
1539
1540 /*PR_Write(output.data, output.len);*/
1541 fwrite(output.Data, output.Length, 1, outFile);
1542 if (cms_verbose) {
1543 fprintf(stderr, "wrote to file\n");
1544 }
1545 SecArenaPoolFree(arena, false);
1546 }
1547
1548 loser:
1549 if (cmsg)
1550 SecCmsMessageDestroy(cmsg);
1551 if (outFile != stdout)
1552 fclose(outFile);
1553
1554 if (decodeOptions.contentFile)
1555 fclose(decodeOptions.contentFile);
1556
1557 return result;
1558 }
1559
1560
1561 #pragma mark ================ Misc from NSS ===================
1562 // from /security/nss/cmd/lib/secutil.c
1563
1564 OSStatus
1565 SECU_FileToItem(CSSM_DATA *dst, FILE *src)
1566 {
1567 const int kReadSize = 4096;
1568 size_t bytesRead, totalRead = 0;
1569
1570 do
1571 {
1572 /* Make room in dst for the new data. */
1573 dst->Length += kReadSize;
1574 dst->Data = realloc(dst->Data, dst->Length);
1575 if (!dst->Data)
1576 return 1 /* @@@ memFullErr */;
1577
1578 bytesRead = fread (&dst->Data[totalRead], 1, kReadSize, src);
1579 totalRead += bytesRead;
1580 } while (bytesRead == kReadSize);
1581
1582 if (!feof (src))
1583 {
1584 /* We are here, but there's no EOF. This is bad */
1585 if (dst->Data) {
1586 free(dst->Data);
1587 dst->Data = NULL;
1588 dst->Length = 0;
1589 }
1590 return 1 /* @@@ ioErr */;
1591 }
1592
1593 /* Trim down the buffer. */
1594 dst->Length = totalRead;
1595 dst->Data = realloc(dst->Data, totalRead);
1596 if (!dst->Data)
1597 return 1 /* @@@ memFullErr */;
1598
1599 return noErr;
1600 }