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