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 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
89static 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
111static 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
155loser:
156    if (policy) CFRelease(policy);
157    return NULL;
158}
159
160static 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
191loser:
192    if (policy) CFRelease(policy);
193    if (search) CFRelease(search);
194
195    return cert;
196}
197
198static 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
230loser:
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);
249loser:
250    if (cert) CFRelease(cert);
251
252    return identity;
253#endif
254}
255
256static 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
264loser:
265    return certificate;
266}
267
268static 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);
288loser:
289    return identityRef;
290}
291
292
293static 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
307extern int doDumpStates;
308#endif /* HAVE_DODUMPSTATES */
309
310OSStatus SECU_FileToItem(CSSM_DATA *dst, FILE *src);
311
312
313extern void SEC_Init(void);		/* XXX */
314static int cms_verbose = 0;
315static int cms_update_single_byte = 0;
316
317/* XXX stolen from cmsarray.c
318 * nss_CMSArray_Count - count number of elements in array
319 */
320int 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
330typedef OSStatus(update_func)(void *cx, const char *data, unsigned int len);
331
332static 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
352static 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
363char *
364ownpw(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
375struct optionsStr {
376    PK11PasswordFunc password;
377    SECCertUsage certUsage;
378    SecKeychainRef certDBHandle;
379};
380
381struct decodeOptionsStr {
382    struct optionsStr *options;
383    FILE *contentFile;
384    int headerLevel;
385    Boolean suppressContent;
386    SecCmsGetDecryptKeyCallback dkcb;
387    SecSymmetricKeyRef bulkkey;
388};
389
390struct 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
403struct envelopeOptionsStr {
404    struct optionsStr *options;
405    char **recipients;
406};
407
408struct certsonlyOptionsStr {
409    struct optionsStr *options;
410    char **recipients;
411};
412
413struct 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
425static SecCmsMessageRef decode(FILE *out, CSSM_DATA *output, CSSM_DATA *input,
426                             const struct decodeOptionsStr *decodeOptions)
427{
428    SecCmsDecoderRef dcx;
429    SecCmsMessageRef cmsg;
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;
575loser:
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/*
583static void
584writeout(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
593static 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
779loser:
780    if (ekpcert) CFRelease(ekpcert);
781    if (cert) CFRelease(cert);
782    if (identity) CFRelease(identity);
783    SecCmsMessageDestroy(cmsg);
784    return NULL;
785}
786
787static 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;
857loser:
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
873SecSymmetricKeyRef dkcb(void *arg, SECAlgorithmID *algid)
874{
875    return (SecSymmetricKeyRef)arg;
876}
877
878static 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
929loser:
930    if (env_cmsg)
931        SecCmsMessageDestroy(env_cmsg);
932
933    return rv;
934}
935
936static 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;
980loser:
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
993static 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;
1047loser:
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
1060typedef enum { UNKNOWN, DECODE, SIGN, ENCRYPT, ENVELOPE, CERTSONLY } Mode;
1061
1062int 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
1543loser:
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
1559OSStatus
1560SECU_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}
1596