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