]> git.saurik.com Git - apple/security.git/blob - SecurityTool/cmsutil.c
Security-57740.31.2.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 SecCmsMessageEncode(env_cmsg, &dummyIn, tmparena, &dummyOut);
906 fwrite(dummyOut.Data, 1, dummyOut.Length,encryptOptions->envFile);
907
908 SecArenaPoolFree(tmparena, false);
909 }
910
911 // get the content info for the enveloped data
912 nlevels = SecCmsMessageContentLevelCount(env_cmsg);
913 for (i = 0; i < nlevels; i++)
914 {
915 SECOidTag typetag;
916 cinfo = SecCmsMessageContentLevel(env_cmsg, i);
917 typetag = SecCmsContentInfoGetContentTypeTag(cinfo);
918 if (typetag == SEC_OID_PKCS7_DATA)
919 {
920 // get the symmetric key
921 encryptOptions->bulkalgtag = SecCmsContentInfoGetContentEncAlgTag(cinfo);
922 encryptOptions->keysize = SecCmsContentInfoGetBulkKeySize(cinfo);
923 encryptOptions->bulkkey = SecCmsContentInfoGetBulkKey(cinfo);
924 rv = noErr;
925 break;
926 }
927 }
928 if (i == nlevels)
929 sec_error("could not retrieve enveloped data: messsage has: %ld levels", nlevels);
930
931 loser:
932 if (env_cmsg)
933 SecCmsMessageDestroy(env_cmsg);
934
935 return rv;
936 }
937
938 static SecCmsMessageRef encrypted_data(struct encryptOptionsStr *encryptOptions)
939 {
940 OSStatus rv = paramErr;
941 SecCmsMessageRef cmsg = NULL;
942 SecCmsContentInfoRef cinfo;
943 SecCmsEncryptedDataRef encd;
944 SecCmsEncoderRef ecx = NULL;
945 SecArenaPoolRef tmppoolp = NULL;
946 CSSM_DATA derOut = { 0, };
947
948 /* arena for output */
949 SEC_CHECK(SecArenaPoolCreate(1024, &tmppoolp), "failed to create arenapool");
950 // create the message object on its own pool
951 SEC_CHECK0(cmsg = SecCmsMessageCreate(NULL), "cannot create CMS message");
952 // build chain of objects: message->encryptedData->data
953 SEC_CHECK0(encd = SecCmsEncryptedDataCreate(cmsg, encryptOptions->bulkalgtag,
954 encryptOptions->keysize),
955 "cannot create CMS encryptedData object");
956 SEC_CHECK0(cinfo = SecCmsMessageGetContentInfo(cmsg),
957 "message has no content info");
958 SEC_CHECK(SecCmsContentInfoSetContentEncryptedData(cmsg, cinfo, encd),
959 "cannot attach CMS encryptedData object");
960 SEC_CHECK0(cinfo = SecCmsEncryptedDataGetContentInfo(encd),
961 "encrypted data has no content info");
962 /* we're always passing data in, so the content is NULL */
963 SEC_CHECK(SecCmsContentInfoSetContentData(cmsg, cinfo, NULL, false),
964 "cannot attach CMS data object");
965 SEC_CHECK(SecCmsEncoderCreate(cmsg, NULL, NULL, &derOut, tmppoolp, NULL, NULL,
966 dkcb, encryptOptions->bulkkey, NULL, NULL, &ecx),
967 "cannot create encoder context");
968 SEC_CHECK(do_update((update_func *)SecCmsEncoderUpdate, ecx, encryptOptions->input->Data,
969 encryptOptions->input->Length),
970 "failed to add data to encoder");
971 SEC_CHECK(SecCmsEncoderFinish(ecx), "failed to encrypt data");
972 fwrite(derOut.Data, derOut.Length, 1, encryptOptions->outfile);
973 /* @@@ Check and report write errors. */
974 /*
975 if (bulkkey)
976 CFRelease(bulkkey);
977 */
978
979 if (tmppoolp)
980 SecArenaPoolFree(tmppoolp, false);
981 return cmsg;
982 loser:
983 /*
984 if (bulkkey)
985 CFRelease(bulkkey);
986 */
987 if (tmppoolp)
988 SecArenaPoolFree(tmppoolp, false);
989 if (cmsg)
990 SecCmsMessageDestroy(cmsg);
991
992 return NULL;
993 }
994
995 static SecCmsMessageRef signed_data_certsonly(struct certsonlyOptionsStr *certsonlyOptions)
996 {
997 SecCmsMessageRef cmsg = NULL;
998 SecCmsContentInfoRef cinfo;
999 SecCmsSignedDataRef sigd;
1000 SecCertificateRef *certs = NULL;
1001 SecKeychainRef dbhandle;
1002 OSStatus rv;
1003
1004 int i = 0, cnt;
1005 dbhandle = certsonlyOptions->options->certDBHandle;
1006 SEC_CHECK0(cnt = nss_CMSArray_Count((void**)certsonlyOptions->recipients),
1007 "please indicate the nickname of a certificate to sign with");
1008 if ((certs = (SecCertificateRef *)calloc((cnt+1), sizeof(SecCertificateRef))) == NULL)
1009 {
1010 sec_error("failed to alloc certs array: %s", strerror(errno));
1011 goto loser;
1012 }
1013 for (i=0; certsonlyOptions->recipients && certsonlyOptions->recipients[i] != NULL; i++)
1014 {
1015 if ((certs[i] = CERT_FindCertByNicknameOrEmailAddr(dbhandle,certsonlyOptions->recipients[i])) == NULL)
1016 {
1017 i=0;
1018 goto loser;
1019 }
1020 }
1021 certs[i] = NULL;
1022 i = 0;
1023 // create the message object on its own pool
1024 SEC_CHECK0(cmsg = SecCmsMessageCreate(NULL), "cannot create CMS message");
1025 // build chain of objects: message->signedData->data
1026 SEC_CHECK0(sigd = SecCmsSignedDataCreateCertsOnly(cmsg, certs[0], true),
1027 "cannot create certs only CMS signedData object");
1028 CFRelease(certs[0]);
1029 for (i = 1; i < cnt; ++i)
1030 {
1031 SEC_CHECK2(SecCmsSignedDataAddCertChain(sigd, certs[i]),
1032 "cannot add cert chain for \"%s\"", certsonlyOptions->recipients[i]);
1033 CFRelease(certs[i]);
1034 }
1035
1036 SEC_CHECK0(cinfo = SecCmsMessageGetContentInfo(cmsg),
1037 "message has no content info");
1038 SEC_CHECK(SecCmsContentInfoSetContentSignedData(cmsg, cinfo, sigd),
1039 "cannot attach CMS signedData object");
1040 SEC_CHECK0(cinfo = SecCmsSignedDataGetContentInfo(sigd),
1041 "signed data has no content info");
1042 SEC_CHECK(SecCmsContentInfoSetContentData(cmsg, cinfo, NULL, false),
1043 "cannot attach CMS data object");
1044
1045 if (certs)
1046 free(certs);
1047
1048 return cmsg;
1049 loser:
1050 if (certs)
1051 {
1052 for (; i < cnt; ++i)
1053 CFRelease(certs[i]);
1054
1055 free(certs);
1056 }
1057 if (cmsg) SecCmsMessageDestroy(cmsg);
1058
1059 return NULL;
1060 }
1061
1062 typedef enum { UNKNOWN, DECODE, SIGN, ENCRYPT, ENVELOPE, CERTSONLY } Mode;
1063
1064 int cms_util(int argc, char * const *argv)
1065 {
1066 FILE *outFile;
1067 SecCmsMessageRef cmsg = NULL;
1068 FILE *inFile;
1069 int ch;
1070 Mode mode = UNKNOWN;
1071 PK11PasswordFunc pwcb;
1072 void *pwcb_arg;
1073 struct decodeOptionsStr decodeOptions = { 0 };
1074 struct signOptionsStr signOptions = { 0 };
1075 struct envelopeOptionsStr envelopeOptions = { 0 };
1076 struct certsonlyOptionsStr certsonlyOptions = { 0 };
1077 struct encryptOptionsStr encryptOptions = { 0 };
1078 struct optionsStr options = { 0 };
1079 int result = 1;
1080 static char *ptrarray[128] = { 0 };
1081 int nrecipients = 0;
1082 char *str, *tok;
1083 char *envFileName;
1084 const char *keychainName = NULL;
1085 CSSM_DATA input = { 0,};
1086 CSSM_DATA output = { 0,};
1087 CSSM_DATA dummy = { 0, };
1088 CSSM_DATA envmsg = { 0, };
1089 OSStatus rv;
1090
1091 inFile = stdin;
1092 outFile = stdout;
1093 envFileName = NULL;
1094 mode = UNKNOWN;
1095 decodeOptions.contentFile = NULL;
1096 decodeOptions.suppressContent = false;
1097 decodeOptions.headerLevel = -1;
1098 options.certUsage = certUsageEmailSigner;
1099 options.password = NULL;
1100 signOptions.nickname = NULL;
1101 signOptions.subjectKeyID = NULL;
1102 signOptions.detached = false;
1103 signOptions.signingTime = false;
1104 signOptions.smimeProfile = false;
1105 signOptions.encryptionKeyPreferenceNick = NULL;
1106 signOptions.hashAlgTag = SEC_OID_SHA1;
1107 envelopeOptions.recipients = NULL;
1108 encryptOptions.recipients = NULL;
1109 encryptOptions.envmsg = NULL;
1110 encryptOptions.envFile = NULL;
1111 encryptOptions.bulkalgtag = SEC_OID_UNKNOWN;
1112 encryptOptions.bulkkey = NULL;
1113 encryptOptions.keysize = -1;
1114
1115 // Parse command line arguments
1116 while ((ch = getopt(argc, argv, "CDEGH:N:OPSTY:Z:c:de:h:i:k:no:p:r:su:vt:")) != -1)
1117 {
1118 switch (ch)
1119 {
1120 case 'C':
1121 mode = ENCRYPT;
1122 break;
1123 case 'D':
1124 mode = DECODE;
1125 break;
1126 case 'E':
1127 mode = ENVELOPE;
1128 break;
1129 case 'G':
1130 if (mode != SIGN) {
1131 sec_error("option -G only supported with option -S");
1132 result = 2; /* Trigger usage message. */
1133 goto loser;
1134 }
1135 signOptions.signingTime = true;
1136 break;
1137 case 'H':
1138 if (mode != SIGN) {
1139 sec_error("option -n only supported with option -D");
1140 result = 2; /* Trigger usage message. */
1141 goto loser;
1142 }
1143 decodeOptions.suppressContent = true;
1144 if (!strcmp(optarg, "MD2"))
1145 signOptions.hashAlgTag = SEC_OID_MD2;
1146 else if (!strcmp(optarg, "MD4"))
1147 signOptions.hashAlgTag = SEC_OID_MD4;
1148 else if (!strcmp(optarg, "MD5"))
1149 signOptions.hashAlgTag = SEC_OID_MD5;
1150 else if (!strcmp(optarg, "SHA1"))
1151 signOptions.hashAlgTag = SEC_OID_SHA1;
1152 else if (!strcmp(optarg, "SHA256"))
1153 signOptions.hashAlgTag = SEC_OID_SHA256;
1154 else if (!strcmp(optarg, "SHA384"))
1155 signOptions.hashAlgTag = SEC_OID_SHA384;
1156 else if (!strcmp(optarg, "SHA512"))
1157 signOptions.hashAlgTag = SEC_OID_SHA512;
1158 else {
1159 sec_error("option -H requires one of MD2,MD4,MD5,SHA1,SHA256,SHA384,SHA512");
1160 goto loser;
1161 }
1162 break;
1163 case 'N':
1164 if (mode != SIGN) {
1165 sec_error("option -N only supported with option -S");
1166 result = 2; /* Trigger usage message. */
1167 goto loser;
1168 }
1169 signOptions.nickname = strdup(optarg);
1170 break;
1171 case 'O':
1172 mode = CERTSONLY;
1173 break;
1174 case 'P':
1175 if (mode != SIGN) {
1176 sec_error("option -P only supported with option -S");
1177 result = 2; /* Trigger usage message. */
1178 goto loser;
1179 }
1180 signOptions.smimeProfile = true;
1181 break;
1182 case 'S':
1183 mode = SIGN;
1184 break;
1185 case 'T':
1186 if (mode != SIGN) {
1187 sec_error("option -T only supported with option -S");
1188 result = 2; /* Trigger usage message. */
1189 goto loser;
1190 }
1191 signOptions.detached = true;
1192 break;
1193 case 'Y':
1194 if (mode != SIGN) {
1195 sec_error("option -Y only supported with option -S");
1196 result = 2; /* Trigger usage message. */
1197 goto loser;
1198 }
1199 signOptions.encryptionKeyPreferenceNick = strdup(optarg);
1200 break;
1201
1202 case 'c':
1203 if (mode != DECODE)
1204 {
1205 sec_error("option -c only supported with option -D");
1206 result = 2; /* Trigger usage message. */
1207 goto loser;
1208 }
1209 if ((decodeOptions.contentFile = fopen(optarg, "rb")) == NULL)
1210 {
1211 sec_error("unable to open \"%s\" for reading: %s", optarg, strerror(errno));
1212 result = 1;
1213 goto loser;
1214 }
1215 break;
1216
1217 #ifdef HAVE_DODUMPSTATES
1218 case 'd':
1219 doDumpStates++;
1220 break;
1221 #endif /* HAVE_DODUMPSTATES */
1222
1223 case 'e':
1224 envFileName = strdup(optarg);
1225 encryptOptions.envFile = fopen(envFileName, "rb"); // PR_RDONLY, 00660);
1226 break;
1227
1228 case 'h':
1229 if (mode != DECODE) {
1230 sec_error("option -h only supported with option -D");
1231 result = 2; /* Trigger usage message. */
1232 goto loser;
1233 }
1234 decodeOptions.headerLevel = atoi(optarg);
1235 if (decodeOptions.headerLevel < 0) {
1236 sec_error("option -h cannot have a negative value");
1237 goto loser;
1238 }
1239 break;
1240 case 'i':
1241 inFile = fopen(optarg,"rb"); // PR_RDONLY, 00660);
1242 if (inFile == NULL)
1243 {
1244 sec_error("unable to open \"%s\" for reading: %s", optarg, strerror(errno));
1245 goto loser;
1246 }
1247 break;
1248
1249 case 'k':
1250 keychainName = optarg;
1251 break;
1252
1253 case 'n':
1254 if (mode != DECODE)
1255 {
1256 sec_error("option -n only supported with option -D");
1257 result = 2; /* Trigger usage message. */
1258 goto loser;
1259 }
1260 decodeOptions.suppressContent = true;
1261 break;
1262 case 'o':
1263 outFile = fopen(optarg, "wb");
1264 if (outFile == NULL)
1265 {
1266 sec_error("unable to open \"%s\" for writing: %s", optarg, strerror(errno));
1267 goto loser;
1268 }
1269 break;
1270 case 'p':
1271 if (!optarg)
1272 {
1273 sec_error("option -p must have a value");
1274 result = 2; /* Trigger usage message. */
1275 goto loser;
1276 }
1277
1278 options.password = (PK11PasswordFunc)ownpw;//strdup(optarg);
1279 break;
1280
1281 case 'r':
1282 if (!optarg)
1283 {
1284 sec_error("option -r must have a value");
1285 result = 2; /* Trigger usage message. */
1286 goto loser;
1287 }
1288
1289 envelopeOptions.recipients = ptrarray;
1290 str = (char *)optarg;
1291 do {
1292 tok = strchr(str, ',');
1293 if (tok) *tok = '\0';
1294 envelopeOptions.recipients[nrecipients++] = strdup(str);
1295 if (tok) str = tok + 1;
1296 } while (tok);
1297 envelopeOptions.recipients[nrecipients] = NULL;
1298 encryptOptions.recipients = envelopeOptions.recipients;
1299 certsonlyOptions.recipients = envelopeOptions.recipients;
1300 break;
1301
1302 case 's':
1303 cms_update_single_byte = 1;
1304 break;
1305
1306 case 'Z':
1307 if (!optarg)
1308 {
1309 sec_error("option -Z must have a value");
1310 result = 2; /* Trigger usage message. */
1311 goto loser;
1312 }
1313 signOptions.subjectKeyID = strdup(optarg);
1314
1315 break;
1316
1317 case 'u':
1318 {
1319 int usageType = atoi (strdup(optarg));
1320 if (usageType < certUsageSSLClient || usageType > certUsageAnyCA)
1321 {
1322 result = 1;
1323 goto loser;
1324 }
1325 options.certUsage = (SECCertUsage)usageType;
1326 break;
1327 }
1328 case 'v':
1329 cms_verbose = 1;
1330 break;
1331 case 't':
1332 if (optarg)
1333 signOptions.timestampingURL = strdup(optarg);
1334 signOptions.wantTimestamping = true;
1335 break;
1336 default:
1337 result = 2; /* Trigger usage message. */
1338 goto loser;
1339 }
1340 }
1341
1342 argc -= optind;
1343 argv += optind;
1344
1345 if (argc != 0 || mode == UNKNOWN)
1346 {
1347 result = 2; /* Trigger usage message. */
1348 goto loser;
1349 }
1350
1351 result = 0;
1352
1353 if (mode != CERTSONLY)
1354 SECU_FileToItem(&input, inFile);
1355 if (inFile != stdin)
1356 fclose(inFile);
1357 if (cms_verbose)
1358 fprintf(stderr, "received commands\n");
1359
1360 /* Call the libsec initialization routines */
1361 if (keychainName)
1362 {
1363 check_obsolete_keychain(keychainName);
1364 options.certDBHandle = keychain_open(keychainName);
1365 if (!options.certDBHandle)
1366 {
1367 sec_perror("SecKeychainOpen", errSecInvalidKeychain);
1368 result = 1;
1369 goto loser;
1370 }
1371 }
1372
1373 if (cms_verbose)
1374 fprintf(stderr, "Got default certdb\n");
1375
1376 switch (mode)
1377 {
1378 case DECODE:
1379 decodeOptions.options = &options;
1380 if (encryptOptions.envFile)
1381 {
1382 /* Decoding encrypted-data, so get the bulkkey from an
1383 * enveloped-data message.
1384 */
1385 SECU_FileToItem(&envmsg, encryptOptions.envFile);
1386 decodeOptions.options = &options;
1387 encryptOptions.envmsg = decode(NULL, &dummy, &envmsg, &decodeOptions);
1388 if (!encryptOptions.envmsg)
1389 {
1390 sec_error("problem decoding env msg");
1391 result = 1;
1392 break;
1393 }
1394 rv = get_enc_params(&encryptOptions);
1395 decodeOptions.dkcb = dkcb;
1396 decodeOptions.bulkkey = encryptOptions.bulkkey;
1397 }
1398 cmsg = decode(outFile, &output, &input, &decodeOptions);
1399 if (!cmsg)
1400 {
1401 sec_error("problem decoding");
1402 result = 1;
1403 }
1404 fwrite(output.Data, output.Length, 1, outFile);
1405 break;
1406 case SIGN:
1407 signOptions.options = &options;
1408 cmsg = signed_data(&signOptions);
1409 if (!cmsg)
1410 {
1411 sec_error("problem signing");
1412 result = 1;
1413 }
1414 break;
1415 case ENCRYPT:
1416 if (!envFileName)
1417 {
1418 sec_error("you must specify an envelope file with -e");
1419 result = 1;
1420 goto loser;
1421 }
1422 encryptOptions.options = &options;
1423 encryptOptions.input = &input;
1424 encryptOptions.outfile = outFile;
1425 if (!encryptOptions.envFile) {
1426 encryptOptions.envFile = fopen(envFileName,"wb"); //PR_WRONLY|PR_CREATE_FILE, 00660);
1427 if (!encryptOptions.envFile)
1428 {
1429 sec_error("failed to create file %s: %s", envFileName, strerror(errno));
1430 result = 1;
1431 goto loser;
1432 }
1433 }
1434 else
1435 {
1436 SECU_FileToItem(&envmsg, encryptOptions.envFile);
1437 decodeOptions.options = &options;
1438 encryptOptions.envmsg = decode(NULL, &dummy, &envmsg,
1439 &decodeOptions);
1440 if (encryptOptions.envmsg == NULL)
1441 {
1442 sec_error("problem decrypting env msg");
1443 result = 1;
1444 break;
1445 }
1446 }
1447
1448 /* decode an enveloped-data message to get the bulkkey (create
1449 * a new one if neccessary)
1450 */
1451 rv = get_enc_params(&encryptOptions);
1452 /* create the encrypted-data message */
1453 cmsg = encrypted_data(&encryptOptions);
1454 if (!cmsg)
1455 {
1456 sec_error("problem encrypting");
1457 result = 1;
1458 }
1459
1460 if (encryptOptions.bulkkey)
1461 {
1462 CFRelease(encryptOptions.bulkkey);
1463 encryptOptions.bulkkey = NULL;
1464 }
1465 break;
1466 case ENVELOPE:
1467 envelopeOptions.options = &options;
1468 cmsg = enveloped_data(&envelopeOptions);
1469 if (!cmsg)
1470 {
1471 sec_error("problem enveloping");
1472 result = 1;
1473 }
1474 break;
1475 case CERTSONLY:
1476 certsonlyOptions.options = &options;
1477 cmsg = signed_data_certsonly(&certsonlyOptions);
1478 if (!cmsg)
1479 {
1480 sec_error("problem with certs-only");
1481 result = 1;
1482 }
1483 break;
1484 case UNKNOWN:
1485 /* Already handled above. */
1486 break;
1487 }
1488
1489 if ( (mode == SIGN || mode == ENVELOPE || mode == CERTSONLY)
1490 && (!result) )
1491 {
1492 SecArenaPoolRef arena = NULL;
1493 SecCmsEncoderRef ecx;
1494 CSSM_DATA output = {};
1495
1496 SEC_CHECK(SecArenaPoolCreate(1024, &arena), "failed to create arenapool");
1497 pwcb = (PK11PasswordFunc)((options.password != NULL) ? ownpw : NULL);
1498 pwcb_arg = (options.password != NULL) ? (void *)options.password : NULL;
1499 if (cms_verbose) {
1500 fprintf(stderr, "cmsg [%p]\n", cmsg);
1501 fprintf(stderr, "arena [%p]\n", arena);
1502 if (pwcb_arg)
1503 fprintf(stderr, "password [%s]\n", (char *)pwcb_arg);
1504 else
1505 fprintf(stderr, "password [NULL]\n");
1506 }
1507
1508 SEC_CHECK(SecCmsEncoderCreate(cmsg,
1509 NULL, NULL, /* DER output callback */
1510 &output, arena, /* destination storage */
1511 pwcb, pwcb_arg, /* password callback */
1512 NULL, NULL, /* decrypt key callback */
1513 NULL, NULL, /* detached digests */
1514 &ecx),
1515 "cannot create encoder context");
1516 if (cms_verbose)
1517 {
1518 fprintf(stderr, "input len [%ld]\n", input.Length);
1519 {
1520 unsigned int j;
1521 for (j = 0; j < input.Length; ++j)
1522 fprintf(stderr, "%2x%c", input.Data[j], (j>0&&j%35==0)?'\n':' ');
1523 }
1524 }
1525
1526 if (input.Length > 0) { /* skip if certs-only (or other zero content) */
1527 SEC_CHECK(SecCmsEncoderUpdate(ecx, (char *)input.Data, input.Length),
1528 "failed to add data to encoder");
1529 }
1530
1531 SEC_CHECK(SecCmsEncoderFinish(ecx), "failed to encode data");
1532
1533 if (cms_verbose) {
1534 fprintf(stderr, "encoding passed\n");
1535 }
1536
1537 /*PR_Write(output.data, output.len);*/
1538 fwrite(output.Data, output.Length, 1, outFile);
1539 if (cms_verbose) {
1540 fprintf(stderr, "wrote to file\n");
1541 }
1542 SecArenaPoolFree(arena, false);
1543 }
1544
1545 loser:
1546 if (cmsg)
1547 SecCmsMessageDestroy(cmsg);
1548 if (outFile != stdout)
1549 fclose(outFile);
1550
1551 if (decodeOptions.contentFile)
1552 fclose(decodeOptions.contentFile);
1553
1554 return result;
1555 }
1556
1557
1558 #pragma mark ================ Misc from NSS ===================
1559 // from /security/nss/cmd/lib/secutil.c
1560
1561 OSStatus
1562 SECU_FileToItem(CSSM_DATA *dst, FILE *src)
1563 {
1564 const int kReadSize = 4096;
1565 size_t bytesRead, totalRead = 0;
1566
1567 do
1568 {
1569 /* Make room in dst for the new data. */
1570 dst->Length += kReadSize;
1571 dst->Data = realloc(dst->Data, dst->Length);
1572 if (!dst->Data)
1573 return 1 /* @@@ memFullErr */;
1574
1575 bytesRead = fread (&dst->Data[totalRead], 1, kReadSize, src);
1576 totalRead += bytesRead;
1577 } while (bytesRead == kReadSize);
1578
1579 if (!feof (src))
1580 {
1581 /* We are here, but there's no EOF. This is bad */
1582 if (dst->Data) {
1583 free(dst->Data);
1584 dst->Data = NULL;
1585 dst->Length = 0;
1586 }
1587 return 1 /* @@@ ioErr */;
1588 }
1589
1590 /* Trim down the buffer. */
1591 dst->Length = totalRead;
1592 dst->Data = realloc(dst->Data, totalRead);
1593 if (!dst->Data)
1594 return 1 /* @@@ memFullErr */;
1595
1596 return noErr;
1597 }