]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_smime/lib/cmssiginfo.c
Security-59754.41.1.tar.gz
[apple/security.git] / OSX / libsecurity_smime / lib / cmssiginfo.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 * CMS signerInfo methods.
36 */
37
38 #include <Security/SecCmsSignerInfo.h>
39 #include "SecSMIMEPriv.h"
40
41 #include "cmslocal.h"
42
43 #include "cert.h"
44 #include "secitem.h"
45 #include "secoid.h"
46 #include "cryptohi.h"
47
48 #include <security_asn1/secasn1.h>
49 #include <security_asn1/secerr.h>
50 #include <Security/SecKeychain.h>
51 #include <Security/SecIdentity.h>
52 #include <Security/SecCertificatePriv.h>
53 #include <Security/SecKeyPriv.h>
54 #include <CoreFoundation/CFTimeZone.h>
55 #include <utilities/SecCFWrappers.h>
56 #include <utilities/debugging.h>
57 #include <AssertMacros.h>
58 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
59 #include <Security/SecPolicyPriv.h>
60 #include <Security/SecItem.h>
61 #include <libDER/asn1Types.h>
62
63 #include "tsaSupport.h"
64 #include "tsaSupportPriv.h"
65
66 #include <syslog.h>
67
68 #define HIDIGIT(v) (((v) / 10) + '0')
69 #define LODIGIT(v) (((v) % 10) + '0')
70
71 #define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9'))
72 #define CAPTURE(var,p,label) \
73 { \
74 if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1])) goto label; \
75 (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \
76 }
77
78 #ifndef NDEBUG
79 #define SIGINFO_DEBUG 1
80 #endif
81
82 #if SIGINFO_DEBUG
83 #define dprintf(args...) fprintf(stderr, args)
84 #else
85 #define dprintf(args...)
86 #endif
87
88 #if RELEASECOUNTDEBUG
89 #define dprintfRC(args...) dprintf(args)
90 #else
91 #define dprintfRC(args...)
92 #endif
93
94 static OSStatus
95 DER_UTCTimeToCFDate(const CSSM_DATA_PTR utcTime, CFAbsoluteTime *date)
96 {
97 CFErrorRef error = NULL;
98 /* <rdar://problem/55316705> CMS attributes don't correctly encode/decode times (always use UTCTime) */
99 CFAbsoluteTime result = SecAbsoluteTimeFromDateContentWithError(ASN1_UTC_TIME, utcTime->Data, utcTime->Length, &error);
100 if (error) {
101 CFReleaseNull(error);
102 return SECFailure;
103 }
104
105 if (date) {
106 *date = result;
107 }
108 return SECSuccess;
109 }
110
111 static OSStatus
112 DER_CFDateToUTCTime(CFAbsoluteTime date, CSSM_DATA_PTR utcTime)
113 {
114 unsigned char *d;
115
116 utcTime->Length = 13;
117 utcTime->Data = d = PORT_Alloc(13);
118 if (!utcTime->Data) {
119 return SECFailure;
120 }
121
122 __block int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
123 __block bool result;
124 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar) {
125 result = CFCalendarDecomposeAbsoluteTime(zuluCalendar, date, "yMdHms", &year, &month, &day, &hour, &minute, &second);
126 });
127 if (!result) {
128 return SECFailure;
129 }
130
131 /* UTC time does not handle the years before 1950 or after 2049 */
132 /* <rdar://problem/55316705> CMS attributes don't correctly encode/decode times (always use UTCTime) */
133 if (year < 1950 || year > 2049) {
134 return SECFailure;
135 }
136
137 /* remove the century since it's added to the year by the
138 CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */
139 year %= 100;
140
141 d[0] = HIDIGIT(year);
142 d[1] = LODIGIT(year);
143 d[2] = HIDIGIT(month);
144 d[3] = LODIGIT(month);
145 d[4] = HIDIGIT(day);
146 d[5] = LODIGIT(day);
147 d[6] = HIDIGIT(hour);
148 d[7] = LODIGIT(hour);
149 d[8] = HIDIGIT(minute);
150 d[9] = LODIGIT(minute);
151 d[10] = HIDIGIT(second);
152 d[11] = LODIGIT(second);
153 d[12] = 'Z';
154 return SECSuccess;
155 }
156
157 /* =============================================================================
158 * SIGNERINFO
159 */
160 SecCmsSignerInfoRef
161 nss_cmssignerinfo_create(SecCmsMessageRef cmsg, SecCmsSignerIDSelector type, SecCertificateRef cert, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag);
162
163 SecCmsSignerInfoRef
164 SecCmsSignerInfoCreateWithSubjKeyID(SecCmsMessageRef cmsg, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag)
165 {
166 return nss_cmssignerinfo_create(cmsg, SecCmsSignerIDSubjectKeyID, NULL, subjKeyID, pubKey, signingKey, digestalgtag);
167 }
168
169 SecCmsSignerInfoRef
170 SecCmsSignerInfoCreate(SecCmsMessageRef cmsg, SecIdentityRef identity, SECOidTag digestalgtag)
171 {
172 SecCmsSignerInfoRef signerInfo = NULL;
173 SecCertificateRef cert = NULL;
174 SecPrivateKeyRef signingKey = NULL;
175 CFDictionaryRef keyAttrs = NULL;
176
177 if (SecIdentityCopyCertificate(identity, &cert))
178 goto loser;
179 if (SecIdentityCopyPrivateKey(identity, &signingKey))
180 goto loser;
181
182 /* In some situations, the "Private Key" in the identity is actually a public key. */
183 keyAttrs = SecKeyCopyAttributes(signingKey);
184 if (!keyAttrs)
185 goto loser;
186 CFTypeRef class = CFDictionaryGetValue(keyAttrs, kSecAttrKeyClass);
187 if (!class || (CFGetTypeID(class) != CFStringGetTypeID()) || !CFEqual(class, kSecAttrKeyClassPrivate))
188 goto loser;
189
190
191 signerInfo = nss_cmssignerinfo_create(cmsg, SecCmsSignerIDIssuerSN, cert, NULL, NULL, signingKey, digestalgtag);
192
193 loser:
194 if (cert)
195 CFRelease(cert);
196 if (signingKey)
197 CFRelease(signingKey);
198 if (keyAttrs)
199 CFRelease(keyAttrs);
200
201 return signerInfo;
202 }
203
204 SecCmsSignerInfoRef
205 nss_cmssignerinfo_create(SecCmsMessageRef cmsg, SecCmsSignerIDSelector type, SecCertificateRef cert, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag)
206 {
207 void *mark;
208 SecCmsSignerInfoRef signerinfo;
209 int version;
210 PLArenaPool *poolp;
211
212 poolp = cmsg->poolp;
213
214 mark = PORT_ArenaMark(poolp);
215
216 signerinfo = (SecCmsSignerInfoRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsSignerInfo));
217 if (signerinfo == NULL) {
218 PORT_ArenaRelease(poolp, mark);
219 return NULL;
220 }
221
222
223 signerinfo->cmsg = cmsg;
224
225 switch(type) {
226 case SecCmsSignerIDIssuerSN:
227 signerinfo->signerIdentifier.identifierType = SecCmsSignerIDIssuerSN;
228 if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL)
229 goto loser;
230 if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL)
231 goto loser;
232 dprintfRC("nss_cmssignerinfo_create: SecCmsSignerIDIssuerSN: cert.rc %d\n",
233 (int)CFGetRetainCount(signerinfo->cert));
234 break;
235 case SecCmsSignerIDSubjectKeyID:
236 signerinfo->signerIdentifier.identifierType = SecCmsSignerIDSubjectKeyID;
237 PORT_Assert(subjKeyID);
238 if (!subjKeyID)
239 goto loser;
240 signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, CSSM_DATA);
241 if (SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
242 subjKeyID)) {
243 goto loser;
244 }
245 signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
246 if (!signerinfo->pubKey)
247 goto loser;
248 break;
249 default:
250 goto loser;
251 }
252
253 if (!signingKey)
254 goto loser;
255
256 signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey);
257 if (!signerinfo->signingKey)
258 goto loser;
259
260 /* set version right now */
261 version = SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN;
262 /* RFC2630 5.3 "version is the syntax version number. If the .... " */
263 if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID)
264 version = SEC_CMS_SIGNER_INFO_VERSION_SUBJKEY;
265 (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version);
266
267 if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess)
268 goto loser;
269
270 PORT_ArenaUnmark(poolp, mark);
271 return signerinfo;
272
273 loser:
274 PORT_ArenaRelease(poolp, mark);
275 return NULL;
276 }
277
278 /*
279 * SecCmsSignerInfoDestroy - destroy a SignerInfo data structure
280 */
281 void
282 SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si)
283 {
284 if (si->cert != NULL) {
285 dprintfRC("SecCmsSignerInfoDestroy top: certp %p cert.rc %d\n",
286 si->cert, (int)CFGetRetainCount(si->cert));
287 CERT_DestroyCertificate(si->cert);
288 }
289 if (si->certList != NULL) {
290 dprintfRC("SecCmsSignerInfoDestroy top: certList.rc %d\n",
291 (int)CFGetRetainCount(si->certList));
292 CFRelease(si->certList);
293 }
294 if (si->timestampCertList != NULL) {
295 dprintfRC("SecCmsSignerInfoDestroy top: timestampCertList.rc %d\n",
296 (int)CFGetRetainCount(si->timestampCertList));
297 CFRelease(si->timestampCertList);
298 }
299 if (si->timestampCert != NULL) {
300 dprintfRC("SecCmsSignerInfoDestroy top: timestampCert.rc %d\n",
301 (int)CFGetRetainCount(si->timestampCert));
302 CFRelease(si->timestampCert);
303 }
304 if (si->hashAgilityAttrValue != NULL) {
305 dprintfRC("SecCmsSignerInfoDestroy top: hashAgilityAttrValue.rc %d\n",
306 (int)CFGetRetainCount(si->hashAgilityAttrValue));
307 CFRelease(si->hashAgilityAttrValue);
308 }
309 if (si->hashAgilityV2AttrValues != NULL) {
310 dprintfRC("SecCmsSignerInfoDestroy top: hashAgilityV2AttrValues.rc %d\n",
311 (int)CFGetRetainCount(si->hashAgilityV2AttrValues));
312 CFRelease(si->hashAgilityV2AttrValues);
313 }
314 /* XXX storage ??? */
315 }
316
317 /*
318 * SecCmsSignerInfoSign - sign something
319 *
320 */
321 OSStatus
322 SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType)
323 {
324 SecCertificateRef cert;
325 SecPrivateKeyRef privkey = NULL;
326 SECOidTag digestalgtag;
327 SECOidTag pubkAlgTag;
328 CSSM_DATA signature = { 0 };
329 OSStatus rv;
330 PLArenaPool *poolp, *tmppoolp = NULL;
331 const SECAlgorithmID *algID;
332 SECAlgorithmID freeAlgID;
333 //CERTSubjectPublicKeyInfo *spki;
334
335 PORT_Assert (digest != NULL);
336
337 poolp = signerinfo->cmsg->poolp;
338
339 switch (signerinfo->signerIdentifier.identifierType) {
340 case SecCmsSignerIDIssuerSN:
341 privkey = signerinfo->signingKey;
342 signerinfo->signingKey = NULL;
343 cert = signerinfo->cert;
344 if (SecCertificateGetAlgorithmID(cert,&algID)) {
345 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
346 goto loser;
347 }
348 break;
349 case SecCmsSignerIDSubjectKeyID:
350 privkey = signerinfo->signingKey;
351 signerinfo->signingKey = NULL;
352 #if 0
353 spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey);
354 SECKEY_DestroyPublicKey(signerinfo->pubKey);
355 signerinfo->pubKey = NULL;
356 SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm);
357 SECKEY_DestroySubjectPublicKeyInfo(spki);
358 algID = &freeAlgID;
359 #else
360
361 #if TARGET_OS_OSX
362 if (SecKeyGetAlgorithmID(signerinfo->pubKey,&algID)) {
363 #else
364 /* TBD: Unify this code. Currently, iOS has an incompatible
365 * SecKeyGetAlgorithmID implementation. */
366 if (true) {
367 #endif
368 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
369 goto loser;
370 }
371 CFRelease(signerinfo->pubKey);
372 signerinfo->pubKey = NULL;
373 #endif
374 break;
375 default:
376 PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE);
377 goto loser;
378 }
379 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
380 /*
381 * XXX I think there should be a cert-level interface for this,
382 * so that I do not have to know about subjectPublicKeyInfo...
383 */
384 pubkAlgTag = SECOID_GetAlgorithmTag(algID);
385 if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) {
386 SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
387 }
388
389 #if 0
390 // @@@ Not yet
391 /* Fortezza MISSI have weird signature formats.
392 * Map them to standard DSA formats
393 */
394 pubkAlgTag = PK11_FortezzaMapSig(pubkAlgTag);
395 #endif
396
397 if (signerinfo->authAttr != NULL) {
398 CSSM_DATA encoded_attrs;
399
400 /* find and fill in the message digest attribute. */
401 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr),
402 SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
403 if (rv != SECSuccess)
404 goto loser;
405
406 if (contentType != NULL) {
407 /* if the caller wants us to, find and fill in the content type attribute. */
408 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr),
409 SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
410 if (rv != SECSuccess)
411 goto loser;
412 }
413
414 if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
415 PORT_SetError(SEC_ERROR_NO_MEMORY);
416 goto loser;
417 }
418
419 /*
420 * Before encoding, reorder the attributes so that when they
421 * are encoded, they will be conforming DER, which is required
422 * to have a specific order and that is what must be used for
423 * the hash/signature. We do this here, rather than building
424 * it into EncodeAttributes, because we do not want to do
425 * such reordering on incoming messages (which also uses
426 * EncodeAttributes) or our old signatures (and other "broken"
427 * implementations) will not verify. So, we want to guarantee
428 * that we send out good DER encodings of attributes, but not
429 * to expect to receive them.
430 */
431 if (SecCmsAttributeArrayReorder(signerinfo->authAttr) != SECSuccess)
432 goto loser;
433
434 encoded_attrs.Data = NULL;
435 encoded_attrs.Length = 0;
436 if (SecCmsAttributeArrayEncode(tmppoolp, &(signerinfo->authAttr),
437 &encoded_attrs) == NULL)
438 goto loser;
439
440 rv = SEC_SignData(&signature, encoded_attrs.Data, (int)encoded_attrs.Length,
441 privkey, digestalgtag, pubkAlgTag);
442 PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
443 tmppoolp = 0;
444 } else {
445 rv = SGN_Digest(privkey, digestalgtag, pubkAlgTag, &signature, digest);
446 }
447 SECKEY_DestroyPrivateKey(privkey);
448 privkey = NULL;
449
450 if (rv != SECSuccess)
451 goto loser;
452
453 if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature)
454 != SECSuccess)
455 goto loser;
456
457 SECITEM_FreeItem(&signature, PR_FALSE);
458
459 SECOidTag sigAlgTag = SecCmsUtilMakeSignatureAlgorithm(digestalgtag, pubkAlgTag);
460 if(pubkAlgTag == SEC_OID_EC_PUBLIC_KEY && SecCmsMsEcdsaCompatMode()) {
461 /*
462 * RFC 3278 section section 2.1.1 states that the signatureAlgorithm
463 * field contains the full ecdsa-with-SHA1 OID, not plain old ecPublicKey
464 * as would appear in other forms of signed datas. However Microsoft doesn't
465 * do this, it puts ecPublicKey there, and if we put ecdsa-with-SHA1 there,
466 * MS can't verify - presumably because it takes the digest of the digest
467 * before feeding it to ECDSA.
468 * We handle this with a preference; default if it's not there is
469 * "Microsoft compatibility mode".
470 */
471 sigAlgTag = SEC_OID_EC_PUBLIC_KEY;
472 }
473
474 if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), sigAlgTag,
475 NULL) != SECSuccess)
476 goto loser;
477
478 return SECSuccess;
479
480 loser:
481 if (signature.Length != 0)
482 SECITEM_FreeItem (&signature, PR_FALSE);
483 if (privkey)
484 SECKEY_DestroyPrivateKey(privkey);
485 if (tmppoolp)
486 PORT_FreeArena(tmppoolp, PR_FALSE);
487 return SECFailure;
488 }
489
490 OSStatus
491 SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray,
492 CFTypeRef policies, SecTrustRef *trustRef)
493 {
494 SecCertificateRef cert;
495 CFAbsoluteTime stime;
496 OSStatus rv;
497 CSSM_DATA_PTR *otherCerts;
498
499 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray)) == NULL) {
500 dprintf("SecCmsSignerInfoVerifyCertificate: no signing cert\n");
501 signerinfo->verificationStatus = SecCmsVSSigningCertNotFound;
502 return SECFailure;
503 }
504
505 /*
506 * Get and convert the signing time; if available, it will be used
507 * both on the cert verification and for importing the sender
508 * email profile.
509 */
510 CFTypeRef timeStampPolicies=SecPolicyCreateAppleTimeStampingAndRevocationPolicies(policies);
511 if (SecCmsSignerInfoGetTimestampTimeWithPolicy(signerinfo, timeStampPolicies, &stime) != SECSuccess)
512 if (SecCmsSignerInfoGetSigningTime(signerinfo, &stime) != SECSuccess)
513 stime = CFAbsoluteTimeGetCurrent();
514 CFReleaseSafe(timeStampPolicies);
515
516 rv = SecCmsSignedDataRawCerts(signerinfo->sigd, &otherCerts);
517 if(rv) {
518 return rv;
519 }
520 rv = CERT_VerifyCert(keychainOrArray, cert, otherCerts, policies, stime, trustRef);
521 dprintfRC("SecCmsSignerInfoVerifyCertificate after vfy: certp %p cert.rc %d\n",
522 cert, (int)CFGetRetainCount(cert));
523 if (rv || !trustRef)
524 {
525 if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT)
526 {
527 /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */
528 if (signerinfo->verificationStatus == SecCmsVSGoodSignature)
529 signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted;
530 }
531 }
532 /* FIXME isn't this leaking the cert? */
533 dprintf("SecCmsSignerInfoVerifyCertificate: CertVerify rtn %d\n", (int)rv);
534 return rv;
535 }
536
537 static void debugShowSigningCertificate(SecCmsSignerInfoRef signerinfo)
538 {
539 #if SIGINFO_DEBUG
540 CFStringRef cn = SecCmsSignerInfoGetSignerCommonName(signerinfo);
541 if (cn)
542 {
543 char *ccn = cfStringToChar(cn);
544 if (ccn)
545 {
546 dprintf("SecCmsSignerInfoVerify: cn: %s\n", ccn);
547 free(ccn);
548 }
549 CFRelease(cn);
550 }
551 #endif
552 }
553
554 /*
555 * SecCmsSignerInfoVerify - verify the signature of a single SignerInfo
556 *
557 * Just verifies the signature. The assumption is that verification of the certificate
558 * is done already.
559 */
560 OSStatus
561 SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType)
562 {
563 return SecCmsSignerInfoVerifyWithPolicy(signerinfo,NULL, digest,contentType);
564 }
565
566 OSStatus
567 SecCmsSignerInfoVerifyWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType)
568 {
569 SecPublicKeyRef publickey = NULL;
570 SecCmsAttribute *attr = NULL;
571 CSSM_DATA encoded_attrs;
572 SecCertificateRef cert = NULL;
573 SecCmsVerificationStatus vs = SecCmsVSUnverified;
574 PLArenaPool *poolp = NULL;
575 SECOidTag digestAlgTag, digestEncAlgTag;
576
577 if (signerinfo == NULL)
578 return SECFailure;
579
580 /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */
581 /* cert has not been verified */
582 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL)) == NULL) {
583 dprintf("SecCmsSignerInfoVerify: no signing cert\n");
584 vs = SecCmsVSSigningCertNotFound;
585 goto loser;
586 }
587
588 dprintfRC("SecCmsSignerInfoVerify top: cert %p cert.rc %d\n", cert, (int)CFGetRetainCount(cert));
589
590 debugShowSigningCertificate(signerinfo);
591
592 if (NULL == (publickey = SecCertificateCopyKey(cert))) {
593 vs = SecCmsVSProcessingError;
594 goto loser;
595 }
596
597 digestAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg));
598 digestEncAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
599
600 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
601 if (contentType) {
602 /*
603 * Check content type
604 *
605 * RFC2630 sez that if there are any authenticated attributes,
606 * then there must be one for content type which matches the
607 * content type of the content being signed, and there must
608 * be one for message digest which matches our message digest.
609 * So check these things first.
610 */
611 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
612 SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE)) == NULL)
613 {
614 vs = SecCmsVSMalformedSignature;
615 goto loser;
616 }
617
618 if (SecCmsAttributeCompareValue(attr, contentType) == PR_FALSE) {
619 vs = SecCmsVSMalformedSignature;
620 goto loser;
621 }
622 }
623
624 /*
625 * Check digest
626 */
627 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE)) == NULL)
628 {
629 vs = SecCmsVSMalformedSignature;
630 goto loser;
631 }
632 if (SecCmsAttributeCompareValue(attr, digest) == PR_FALSE) {
633 vs = SecCmsVSDigestMismatch;
634 goto loser;
635 }
636
637 if ((poolp = PORT_NewArena (1024)) == NULL) {
638 vs = SecCmsVSProcessingError;
639 goto loser;
640 }
641
642 /*
643 * Check signature
644 *
645 * The signature is based on a digest of the DER-encoded authenticated
646 * attributes. So, first we encode and then we digest/verify.
647 * we trust the decoder to have the attributes in the right (sorted) order
648 */
649 encoded_attrs.Data = NULL;
650 encoded_attrs.Length = 0;
651
652 if (SecCmsAttributeArrayEncode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL ||
653 encoded_attrs.Data == NULL || encoded_attrs.Length == 0)
654 {
655 vs = SecCmsVSProcessingError;
656 goto loser;
657 }
658
659 vs = (VFY_VerifyData (encoded_attrs.Data, (int)encoded_attrs.Length,
660 publickey, &(signerinfo->encDigest),
661 digestAlgTag, digestEncAlgTag,
662 signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature;
663
664 dprintf("VFY_VerifyData (authenticated attributes): %s\n",
665 (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature");
666
667 PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */
668
669 } else {
670 CSSM_DATA_PTR sig;
671
672 /* No authenticated attributes. The signature is based on the plain message digest. */
673 sig = &(signerinfo->encDigest);
674 if (sig->Length == 0)
675 goto loser;
676
677 vs = (VFY_VerifyDigest(digest, publickey, sig,
678 digestAlgTag, digestEncAlgTag,
679 signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature;
680
681 dprintf("VFY_VerifyData (plain message digest): %s\n",
682 (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature");
683 }
684
685 if (!SecCmsArrayIsEmpty((void **)signerinfo->unAuthAttr))
686 {
687 dprintf("found an unAuthAttr\n");
688 OSStatus rux = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo,timeStampPolicy);
689 dprintf("SecCmsSignerInfoVerifyUnAuthAttrs Status: %ld\n", (long)rux);
690 if (rux) {
691 goto loser;
692 }
693 }
694
695 if (vs == SecCmsVSBadSignature) {
696 /*
697 * XXX Change the generic error into our specific one, because
698 * in that case we get a better explanation out of the Security
699 * Advisor. This is really a bug in our error strings (the
700 * "generic" error has a lousy/wrong message associated with it
701 * which assumes the signature verification was done for the
702 * purposes of checking the issuer signature on a certificate)
703 * but this is at least an easy workaround and/or in the
704 * Security Advisor, which specifically checks for the error
705 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
706 * in that case but does not similarly check for
707 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would
708 * probably say the wrong thing in the case that it *was* the
709 * certificate signature check that failed during the cert
710 * verification done above. Our error handling is really a mess.
711 */
712 if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
713 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
714 }
715
716 if (publickey != NULL)
717 CFRelease(publickey);
718
719 signerinfo->verificationStatus = vs;
720 dprintfRC("SecCmsSignerInfoVerify end: cerp %p cert.rc %d\n",
721 cert, (int)CFGetRetainCount(cert));
722
723 dprintf("verificationStatus: %d\n", vs);
724
725 return (vs == SecCmsVSGoodSignature) ? SECSuccess : SECFailure;
726
727 loser:
728 if (publickey != NULL)
729 SECKEY_DestroyPublicKey (publickey);
730
731 dprintf("verificationStatus2: %d\n", vs);
732 signerinfo->verificationStatus = vs;
733
734 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
735 return SECFailure;
736 }
737
738 OSStatus
739 SecCmsSignerInfoVerifyUnAuthAttrs(SecCmsSignerInfoRef signerinfo) {
740 return SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo, NULL);
741 }
742
743 OSStatus
744 SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy)
745 {
746 /*
747 unAuthAttr is an array of attributes; we expect to
748 see just one: the timestamp blob. If we have an unAuthAttr,
749 but don't see a timestamp, return an error since we have
750 no other cases where this would be present.
751 */
752
753 SecCmsAttribute *attr = NULL;
754 OSStatus status = SECFailure;
755
756 require(signerinfo, xit);
757 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->unAuthAttr,
758 SEC_OID_PKCS9_TIMESTAMP_TOKEN, PR_TRUE);
759 if (attr == NULL)
760 {
761 status = errSecTimestampMissing;
762 goto xit;
763 }
764
765 dprintf("found an id-ct-TSTInfo\n");
766 // Don't check the nonce in this case
767 status = decodeTimeStampTokenWithPolicy(signerinfo, timeStampPolicy, (attr->values)[0], &signerinfo->encDigest, 0);
768 if (status != errSecSuccess) {
769 secerror("timestamp verification failed: %d", (int)status);
770 }
771
772 xit:
773 return status;
774 }
775
776 CSSM_DATA *
777 SecCmsSignerInfoGetEncDigest(SecCmsSignerInfoRef signerinfo)
778 {
779 return &signerinfo->encDigest;
780 }
781
782 SecCmsVerificationStatus
783 SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo)
784 {
785 return signerinfo->verificationStatus;
786 }
787
788 SECOidData *
789 SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo)
790 {
791 return SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
792 }
793
794 SECOidTag
795 SecCmsSignerInfoGetDigestAlgTag(SecCmsSignerInfoRef signerinfo)
796 {
797 SECOidData *algdata;
798
799 algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
800 if (algdata != NULL)
801 return algdata->offset;
802 else
803 return SEC_OID_UNKNOWN;
804 }
805
806 CFArrayRef
807 SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo)
808 {
809 dprintfRC("SecCmsSignerInfoGetCertList: certList.rc %d\n",
810 (int)CFGetRetainCount(signerinfo->certList));
811 return signerinfo->certList;
812 }
813
814 CFArrayRef
815 SecCmsSignerInfoGetTimestampCertList(SecCmsSignerInfoRef signerinfo)
816 {
817 dprintfRC("SecCmsSignerInfoGetTimestampCertList: timestampCertList.rc %d\n",
818 (int)CFGetRetainCount(signerinfo->timestampCertList));
819 return signerinfo->timestampCertList;
820 }
821
822 SecCertificateRef
823 SecCmsSignerInfoGetTimestampSigningCert(SecCmsSignerInfoRef signerinfo)
824 {
825 dprintfRC("SecCmsSignerInfoGetTimestampSigningCert: timestampCert.rc %d\n",
826 (int)CFGetRetainCount(signerinfo->timestampCert));
827 return signerinfo->timestampCert;
828 }
829
830 int
831 SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo)
832 {
833 unsigned long version;
834
835 /* always take apart the CSSM_DATA */
836 if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
837 return 0;
838 else
839 return (int)version;
840 }
841
842 /*
843 * SecCmsSignerInfoGetSigningTime - return the signing time,
844 * in UTCTime format, of a CMS signerInfo.
845 *
846 * sinfo - signerInfo data for this signer
847 *
848 * Returns a pointer to XXXX (what?)
849 * A return value of NULL is an error.
850 */
851 OSStatus
852 SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime)
853 {
854 SecCmsAttribute *attr;
855 CSSM_DATA_PTR value;
856
857 if (sinfo == NULL)
858 return paramErr;
859
860 if (sinfo->signingTime != 0) {
861 *stime = sinfo->signingTime; /* cached copy */
862 return SECSuccess;
863 }
864
865 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
866 /* XXXX multi-valued attributes NIH */
867 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
868 return errSecSigningTimeMissing;
869 if (DER_UTCTimeToCFDate(value, stime) != SECSuccess)
870 return errSecSigningTimeMissing;
871 sinfo->signingTime = *stime; /* make cached copy */
872 return SECSuccess;
873 }
874
875 OSStatus
876 SecCmsSignerInfoGetTimestampTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime)
877 {
878 return SecCmsSignerInfoGetTimestampTimeWithPolicy(sinfo, NULL, stime);
879 }
880
881 OSStatus
882 SecCmsSignerInfoGetTimestampTimeWithPolicy(SecCmsSignerInfoRef sinfo, CFTypeRef timeStampPolicy, CFAbsoluteTime *stime)
883 {
884 OSStatus status = paramErr;
885
886 require(sinfo && stime, xit);
887
888 if (sinfo->timestampTime != 0)
889 {
890 *stime = sinfo->timestampTime; /* cached copy */
891 return noErr;
892 }
893
894 // A bit heavyweight if haven't already called verify
895 status = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(sinfo,timeStampPolicy);
896 *stime = sinfo->timestampTime;
897 xit:
898 return status;
899 }
900
901 /*!
902 @function
903 @abstract Return the data in the signed Codesigning Hash Agility attribute.
904 @param sinfo SignerInfo data for this signer, pointer to a CFDataRef for attribute value
905 @discussion Returns a CFDataRef containing the value of the attribute
906 @result A return value of errSecInternal is an error trying to look up the oid.
907 A status value of success with null result data indicates the attribute was not present.
908 */
909 OSStatus
910 SecCmsSignerInfoGetAppleCodesigningHashAgility(SecCmsSignerInfoRef sinfo, CFDataRef *sdata)
911 {
912 SecCmsAttribute *attr;
913 CSSM_DATA_PTR value;
914
915 if (sinfo == NULL || sdata == NULL)
916 return paramErr;
917
918 *sdata = NULL;
919
920 if (sinfo->hashAgilityAttrValue != NULL) {
921 *sdata = sinfo->hashAgilityAttrValue; /* cached copy */
922 return SECSuccess;
923 }
924
925 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_HASH_AGILITY, PR_TRUE);
926
927 /* attribute not found */
928 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
929 return SECSuccess;
930
931 sinfo->hashAgilityAttrValue = CFDataCreate(NULL, value->Data, value->Length); /* make cached copy */
932 if (sinfo->hashAgilityAttrValue) {
933 *sdata = sinfo->hashAgilityAttrValue;
934 return SECSuccess;
935 }
936 return errSecAllocate;
937 }
938
939 /* AgileHash ::= SEQUENCE {
940 hashType OBJECT IDENTIFIER,
941 hashValues OCTET STRING }
942 */
943 typedef struct {
944 SecAsn1Item digestOID;
945 SecAsn1Item digestValue;
946 } CMSAppleAgileHash;
947
948 static const SecAsn1Template CMSAppleAgileHashTemplate[] = {
949 { SEC_ASN1_SEQUENCE,
950 0, NULL, sizeof(CMSAppleAgileHash) },
951 { SEC_ASN1_OBJECT_ID,
952 offsetof(CMSAppleAgileHash, digestOID), },
953 { SEC_ASN1_OCTET_STRING,
954 offsetof(CMSAppleAgileHash, digestValue), },
955 { 0, }
956 };
957
958 static OSStatus CMSAddAgileHashToDictionary(CFMutableDictionaryRef dictionary, SecAsn1Item *DERAgileHash) {
959 PLArenaPool *tmppoolp = NULL;
960 OSStatus status = errSecSuccess;
961 CMSAppleAgileHash agileHash;
962 CFDataRef digestValue = NULL;
963 CFNumberRef digestTag = NULL;
964
965 tmppoolp = PORT_NewArena(1024);
966 if (tmppoolp == NULL) {
967 return errSecAllocate;
968 }
969
970 if ((status = SEC_ASN1DecodeItem(tmppoolp, &agileHash, CMSAppleAgileHashTemplate, DERAgileHash)) != errSecSuccess) {
971 goto loser;
972 }
973
974 int64_t tag = SECOID_FindOIDTag(&agileHash.digestOID);
975 digestTag = CFNumberCreate(NULL, kCFNumberSInt64Type, &tag);
976 digestValue = CFDataCreate(NULL, agileHash.digestValue.Data, agileHash.digestValue.Length);
977 CFDictionaryAddValue(dictionary, digestTag, digestValue);
978
979 loser:
980 CFReleaseNull(digestValue);
981 CFReleaseNull(digestTag);
982 if (tmppoolp) {
983 PORT_FreeArena(tmppoolp, PR_FALSE);
984 }
985 return status;
986 }
987
988 /*!
989 @function
990 @abstract Return the data in the signed Codesigning Hash Agility V2 attribute.
991 @param sinfo SignerInfo data for this signer, pointer to a CFDictionaryRef for attribute values
992 @discussion Returns a CFDictionaryRef containing the values of the attribute
993 @result A return value of errSecInternal is an error trying to look up the oid.
994 A status value of success with null result data indicates the attribute was not present.
995 */
996 OSStatus
997 SecCmsSignerInfoGetAppleCodesigningHashAgilityV2(SecCmsSignerInfoRef sinfo, CFDictionaryRef *sdict)
998 {
999 SecCmsAttribute *attr;
1000
1001 if (sinfo == NULL || sdict == NULL) {
1002 return errSecParam;
1003 }
1004
1005 *sdict = NULL;
1006
1007 if (sinfo->hashAgilityV2AttrValues != NULL) {
1008 *sdict = sinfo->hashAgilityV2AttrValues; /* cached copy */
1009 return SECSuccess;
1010 }
1011
1012 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_HASH_AGILITY_V2, PR_TRUE);
1013
1014 /* attribute not found */
1015 if (attr == NULL) {
1016 return SECSuccess;
1017 }
1018
1019 /* attrValues SET OF AttributeValue
1020 * AttributeValue ::= ANY
1021 */
1022 CSSM_DATA_PTR *values = attr->values;
1023 if (values == NULL) { /* There must be values */
1024 return errSecDecode;
1025 }
1026
1027 CFMutableDictionaryRef agileHashValues = CFDictionaryCreateMutable(NULL, SecCmsArrayCount((void **)values),
1028 &kCFTypeDictionaryKeyCallBacks,
1029 &kCFTypeDictionaryValueCallBacks);
1030 while (*values != NULL) {
1031 (void)CMSAddAgileHashToDictionary(agileHashValues, *values++);
1032 }
1033 if (CFDictionaryGetCount(agileHashValues) != SecCmsArrayCount((void **)attr->values)) {
1034 CFReleaseNull(agileHashValues);
1035 return errSecDecode;
1036 }
1037
1038 sinfo->hashAgilityV2AttrValues = agileHashValues; /* make cached copy */
1039 if (sinfo->hashAgilityV2AttrValues) {
1040 *sdict = sinfo->hashAgilityV2AttrValues;
1041 return SECSuccess;
1042 }
1043 return errSecAllocate;
1044 }
1045
1046 /*
1047 * SecCmsSignerInfoGetAppleExpirationTime - return the expiration time,
1048 * in UTCTime format, of a CMS signerInfo.
1049 *
1050 * sinfo - signerInfo data for this signer
1051 *
1052 * Returns a pointer to XXXX (what?)
1053 * A return value of NULL is an error.
1054 */
1055 OSStatus
1056 SecCmsSignerInfoGetAppleExpirationTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *etime)
1057 {
1058 SecCmsAttribute *attr = NULL;
1059 SecAsn1Item * value = NULL;
1060
1061 if (sinfo == NULL || etime == NULL) {
1062 return SECFailure;
1063 }
1064
1065 if (sinfo->expirationTime != 0) {
1066 *etime = sinfo->expirationTime; /* cached copy */
1067 return SECSuccess;
1068 }
1069
1070 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_EXPIRATION_TIME, PR_TRUE);
1071 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL) {
1072 return SECFailure;
1073 }
1074 if (DER_UTCTimeToCFDate(value, etime) != SECSuccess) {
1075 return SECFailure;
1076 }
1077 sinfo->expirationTime = *etime; /* make cached copy */
1078 return SECSuccess;
1079 }
1080
1081 /*
1082 * Return the signing cert of a CMS signerInfo.
1083 *
1084 * the certs in the enclosing SignedData must have been imported already
1085 */
1086 SecCertificateRef
1087 SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray)
1088 {
1089 SecCertificateRef cert;
1090 SecCmsSignerIdentifier *sid;
1091 OSStatus ortn;
1092 CSSM_DATA_PTR *rawCerts;
1093
1094 if (signerinfo->cert != NULL) {
1095 dprintfRC("SecCmsSignerInfoGetSigningCertificate top: cert %p cert.rc %d\n",
1096 signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert));
1097 return signerinfo->cert;
1098 }
1099 ortn = SecCmsSignedDataRawCerts(signerinfo->sigd, &rawCerts);
1100 if(ortn) {
1101 return NULL;
1102 }
1103 dprintf("SecCmsSignerInfoGetSigningCertificate: numRawCerts %d\n",
1104 SecCmsArrayCount((void **)rawCerts));
1105
1106 /*
1107 * This cert will also need to be freed, but since we save it
1108 * in signerinfo for later, we do not want to destroy it when
1109 * we leave this function -- we let the clean-up of the entire
1110 * cinfo structure later do the destroy of this cert.
1111 */
1112 sid = &signerinfo->signerIdentifier;
1113 switch (sid->identifierType) {
1114 case SecCmsSignerIDIssuerSN:
1115 cert = CERT_FindCertByIssuerAndSN(keychainOrArray, rawCerts, signerinfo->sigd->certs, signerinfo->cmsg->poolp,
1116 sid->id.issuerAndSN);
1117 break;
1118 case SecCmsSignerIDSubjectKeyID:
1119 cert = CERT_FindCertBySubjectKeyID(keychainOrArray, rawCerts, signerinfo->sigd->certs, sid->id.subjectKeyID);
1120 break;
1121 default:
1122 cert = NULL;
1123 break;
1124 }
1125
1126 /* cert can be NULL at that point */
1127 signerinfo->cert = cert; /* earmark it */
1128 dprintfRC("SecCmsSignerInfoGetSigningCertificate end: certp %p cert.rc %d\n",
1129 signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert));
1130
1131 return cert;
1132 }
1133
1134 /*
1135 * SecCmsSignerInfoGetSignerCommonName - return the common name of the signer
1136 *
1137 * sinfo - signerInfo data for this signer
1138 *
1139 * Returns a CFStringRef containing the common name of the signer.
1140 * A return value of NULL is an error.
1141 */
1142 CFStringRef
1143 SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo)
1144 {
1145 SecCertificateRef signercert;
1146 CFStringRef commonName = NULL;
1147
1148 /* will fail if cert is not verified */
1149 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
1150 return NULL;
1151
1152 if (errSecSuccess != SecCertificateCopyCommonName(signercert, &commonName)) {
1153 return NULL;
1154 }
1155
1156 return commonName;
1157 }
1158
1159 /*
1160 * SecCmsSignerInfoGetSignerEmailAddress - return the email address of the signer
1161 *
1162 * sinfo - signerInfo data for this signer
1163 *
1164 * Returns a CFStringRef containing the name of the signer.
1165 * A return value of NULL is an error.
1166 */
1167 CFStringRef
1168 SecCmsSignerInfoGetSignerEmailAddress(SecCmsSignerInfoRef sinfo)
1169 {
1170 SecCertificateRef signercert;
1171 CFStringRef emailAddress = NULL;
1172
1173 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL) {
1174 return NULL;
1175 }
1176
1177 CFArrayRef names = SecCertificateCopyRFC822Names(signercert);
1178 if (names) {
1179 if (CFArrayGetCount(names) > 0) {
1180 emailAddress = (CFStringRef)CFArrayGetValueAtIndex(names, 0);
1181 }
1182 CFRetainSafe(emailAddress);
1183 CFRelease(names);
1184 }
1185 return emailAddress;
1186 }
1187
1188
1189 /*
1190 * SecCmsSignerInfoAddAuthAttr - add an attribute to the
1191 * authenticated (i.e. signed) attributes of "signerinfo".
1192 */
1193 OSStatus
1194 SecCmsSignerInfoAddAuthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
1195 {
1196 return SecCmsAttributeArrayAddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
1197 }
1198
1199 /*
1200 * SecCmsSignerInfoAddUnauthAttr - add an attribute to the
1201 * unauthenticated attributes of "signerinfo".
1202 */
1203 OSStatus
1204 SecCmsSignerInfoAddUnauthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
1205 {
1206 return SecCmsAttributeArrayAddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
1207 }
1208
1209 /*
1210 * SecCmsSignerInfoAddSigningTime - add the signing time to the
1211 * authenticated (i.e. signed) attributes of "signerinfo".
1212 *
1213 * This is expected to be included in outgoing signed
1214 * messages for email (S/MIME) but is likely useful in other situations.
1215 *
1216 * This should only be added once; a second call will do nothing.
1217 *
1218 * XXX This will probably just shove the current time into "signerinfo"
1219 * but it will not actually get signed until the entire item is
1220 * processed for encoding. Is this (expected to be small) delay okay?
1221 */
1222 OSStatus
1223 SecCmsSignerInfoAddSigningTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t)
1224 {
1225 SecCmsAttribute *attr;
1226 CSSM_DATA stime;
1227 void *mark;
1228 PLArenaPool *poolp;
1229
1230 poolp = signerinfo->cmsg->poolp;
1231
1232 mark = PORT_ArenaMark(poolp);
1233
1234 /* create new signing time attribute */
1235 if (DER_CFDateToUTCTime(t, &stime) != SECSuccess)
1236 goto loser;
1237
1238 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
1239 SECITEM_FreeItem (&stime, PR_FALSE);
1240 goto loser;
1241 }
1242
1243 SECITEM_FreeItem (&stime, PR_FALSE);
1244
1245 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1246 goto loser;
1247
1248 PORT_ArenaUnmark (poolp, mark);
1249
1250 return SECSuccess;
1251
1252 loser:
1253 PORT_ArenaRelease (poolp, mark);
1254 return SECFailure;
1255 }
1256
1257 /*
1258 * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the
1259 * authenticated (i.e. signed) attributes of "signerinfo".
1260 *
1261 * This is expected to be included in outgoing signed
1262 * messages for email (S/MIME).
1263 */
1264 OSStatus
1265 SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo)
1266 {
1267 SecCmsAttribute *attr;
1268 CSSM_DATA_PTR smimecaps = NULL;
1269 void *mark;
1270 PLArenaPool *poolp;
1271
1272 poolp = signerinfo->cmsg->poolp;
1273
1274 mark = PORT_ArenaMark(poolp);
1275
1276 smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
1277 if (smimecaps == NULL)
1278 goto loser;
1279
1280 /* create new signing time attribute */
1281 #if 1
1282 // @@@ We don't do Fortezza yet.
1283 if (SecSMIMECreateSMIMECapabilities((SecArenaPoolRef)poolp, smimecaps, PR_FALSE) != SECSuccess)
1284 #else
1285 if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps,
1286 PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess)
1287 #endif
1288 goto loser;
1289
1290 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
1291 goto loser;
1292
1293 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1294 goto loser;
1295
1296 PORT_ArenaUnmark (poolp, mark);
1297 return SECSuccess;
1298
1299 loser:
1300 PORT_ArenaRelease (poolp, mark);
1301 return SECFailure;
1302 }
1303
1304 /*
1305 * SecCmsSignerInfoAddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1306 * authenticated (i.e. signed) attributes of "signerinfo".
1307 *
1308 * This is expected to be included in outgoing signed messages for email (S/MIME).
1309 */
1310 OSStatus
1311 SecCmsSignerInfoAddSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1312 {
1313 SecCmsAttribute *attr;
1314 CSSM_DATA_PTR smimeekp = NULL;
1315 void *mark;
1316 PLArenaPool *poolp;
1317
1318 #if 0
1319 CFTypeRef policy;
1320
1321 /* verify this cert for encryption */
1322 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1323 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1324 CFRelease(policy);
1325 return SECFailure;
1326 }
1327 CFRelease(policy);
1328 #endif
1329
1330 poolp = signerinfo->cmsg->poolp;
1331 mark = PORT_ArenaMark(poolp);
1332
1333 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1334 if (smimeekp == NULL)
1335 goto loser;
1336
1337 /* create new signing time attribute */
1338 if (SecSMIMECreateSMIMEEncKeyPrefs((SecArenaPoolRef)poolp, smimeekp, cert) != SECSuccess)
1339 goto loser;
1340
1341 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1342 goto loser;
1343
1344 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1345 goto loser;
1346
1347 PORT_ArenaUnmark (poolp, mark);
1348 return SECSuccess;
1349
1350 loser:
1351 PORT_ArenaRelease (poolp, mark);
1352 return SECFailure;
1353 }
1354
1355 /*
1356 * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1357 * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft.
1358 *
1359 * This is expected to be included in outgoing signed messages for email (S/MIME),
1360 * if compatibility with Microsoft mail clients is wanted.
1361 */
1362 OSStatus
1363 SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1364 {
1365 SecCmsAttribute *attr;
1366 CSSM_DATA_PTR smimeekp = NULL;
1367 void *mark;
1368 PLArenaPool *poolp;
1369
1370 #if 0
1371 CFTypeRef policy;
1372
1373 /* verify this cert for encryption */
1374 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1375 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1376 CFRelease(policy);
1377 return SECFailure;
1378 }
1379 CFRelease(policy);
1380 #endif
1381
1382 poolp = signerinfo->cmsg->poolp;
1383 mark = PORT_ArenaMark(poolp);
1384
1385 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1386 if (smimeekp == NULL)
1387 goto loser;
1388
1389 /* create new signing time attribute */
1390 if (SecSMIMECreateMSSMIMEEncKeyPrefs((SecArenaPoolRef)poolp, smimeekp, cert) != SECSuccess)
1391 goto loser;
1392
1393 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1394 goto loser;
1395
1396 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1397 goto loser;
1398
1399 PORT_ArenaUnmark (poolp, mark);
1400 return SECSuccess;
1401
1402 loser:
1403 PORT_ArenaRelease (poolp, mark);
1404 return SECFailure;
1405 }
1406
1407 /*
1408 * SecCmsSignerInfoAddTimeStamp - add time stamp to the
1409 * unauthenticated (i.e. unsigned) attributes of "signerinfo".
1410 *
1411 * This will initially be used for time stamping signed applications
1412 * by using a Time Stamping Authority. It may also be included in outgoing signed
1413 * messages for email (S/MIME), and may be useful in other situations.
1414 *
1415 * This should only be added once; a second call will do nothing.
1416 *
1417 */
1418
1419 /*
1420 Countersignature attribute values have ASN.1 type Countersignature:
1421 Countersignature ::= SignerInfo
1422 Countersignature values have the same meaning as SignerInfo values
1423 for ordinary signatures, except that:
1424 1. The signedAttributes field MUST NOT contain a content-type
1425 attribute; there is no content type for countersignatures.
1426 2. The signedAttributes field MUST contain a message-digest
1427 attribute if it contains any other attributes.
1428 3. The input to the message-digesting process is the contents octets
1429 of the DER encoding of the signatureValue field of the SignerInfo
1430 value with which the attribute is associated.
1431 */
1432
1433 /*!
1434 @function
1435 @abstract Create a timestamp unsigned attribute with a TimeStampToken.
1436 */
1437
1438 OSStatus
1439 SecCmsSignerInfoAddTimeStamp(SecCmsSignerInfoRef signerinfo, CSSM_DATA *tstoken)
1440 {
1441 SecCmsAttribute *attr;
1442 PLArenaPool *poolp = signerinfo->cmsg->poolp;
1443 void *mark = PORT_ArenaMark(poolp);
1444
1445 // We have already encoded this ourselves, so last param is PR_TRUE
1446 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_TIMESTAMP_TOKEN, tstoken, PR_TRUE)) == NULL)
1447 goto loser;
1448
1449 if (SecCmsSignerInfoAddUnauthAttr(signerinfo, attr) != SECSuccess)
1450 goto loser;
1451
1452 PORT_ArenaUnmark (poolp, mark);
1453
1454 return SECSuccess;
1455
1456 loser:
1457 PORT_ArenaRelease (poolp, mark);
1458 return SECFailure;
1459 }
1460
1461 /*
1462 * SecCmsSignerInfoAddCounterSignature - countersign a signerinfo
1463 *
1464 * 1. digest the DER-encoded signature value of the original signerinfo
1465 * 2. create new signerinfo with correct version, sid, digestAlg
1466 * 3. add message-digest authAttr, but NO content-type
1467 * 4. sign the authAttrs
1468 * 5. DER-encode the new signerInfo
1469 * 6. add the whole thing to original signerInfo's unAuthAttrs
1470 * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
1471 *
1472 * XXXX give back the new signerinfo?
1473 */
1474 OSStatus
1475 SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo,
1476 SECOidTag digestalg, SecIdentityRef identity)
1477 {
1478 /* XXXX TBD XXXX */
1479 return SECFailure;
1480 }
1481
1482 /*!
1483 @function
1484 @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
1485 @discussion This is expected to be included in outgoing Apple code signatures.
1486 */
1487 OSStatus
1488 SecCmsSignerInfoAddAppleCodesigningHashAgility(SecCmsSignerInfoRef signerinfo, CFDataRef attrValue)
1489 {
1490 SecCmsAttribute *attr;
1491 PLArenaPool *poolp = signerinfo->cmsg->poolp;
1492 void *mark = PORT_ArenaMark(poolp);
1493 OSStatus status = SECFailure;
1494
1495 /* The value is required for this attribute. */
1496 if (!attrValue) {
1497 status = errSecParam;
1498 goto loser;
1499 }
1500
1501 /*
1502 * SecCmsAttributeCreate makes a copy of the data in value, so
1503 * we don't need to copy into the CSSM_DATA struct.
1504 */
1505 CSSM_DATA value;
1506 value.Length = CFDataGetLength(attrValue);
1507 value.Data = (uint8_t *)CFDataGetBytePtr(attrValue);
1508
1509 if ((attr = SecCmsAttributeCreate(poolp,
1510 SEC_OID_APPLE_HASH_AGILITY,
1511 &value,
1512 PR_FALSE)) == NULL) {
1513 status = errSecAllocate;
1514 goto loser;
1515 }
1516
1517 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
1518 status = errSecInternalError;
1519 goto loser;
1520 }
1521
1522 PORT_ArenaUnmark(poolp, mark);
1523 return SECSuccess;
1524
1525 loser:
1526 PORT_ArenaRelease(poolp, mark);
1527 return status;
1528 }
1529
1530 static OSStatus CMSAddAgileHashToAttribute(PLArenaPool *poolp, SecCmsAttribute *attr, CFNumberRef cftag, CFDataRef value) {
1531 PLArenaPool *tmppoolp = NULL;
1532 int64_t tag;
1533 SECOidData *digestOid = NULL;
1534 CMSAppleAgileHash agileHash;
1535 SecAsn1Item attrValue = { .Data = NULL, .Length = 0 };
1536 OSStatus status = errSecSuccess;
1537
1538 memset(&agileHash, 0, sizeof(agileHash));
1539
1540 if(!CFNumberGetValue(cftag, kCFNumberSInt64Type, &tag)) {
1541 return errSecParam;
1542 }
1543 digestOid = SECOID_FindOIDByTag((SECOidTag)tag);
1544
1545 agileHash.digestValue.Data = (uint8_t *)CFDataGetBytePtr(value);
1546 agileHash.digestValue.Length = CFDataGetLength(value);
1547 agileHash.digestOID.Data = digestOid->oid.Data;
1548 agileHash.digestOID.Length = digestOid->oid.Length;
1549
1550 tmppoolp = PORT_NewArena(1024);
1551 if (tmppoolp == NULL) {
1552 return errSecAllocate;
1553 }
1554
1555 if (SEC_ASN1EncodeItem(tmppoolp, &attrValue, &agileHash, CMSAppleAgileHashTemplate) == NULL) {
1556 status = errSecParam;
1557 goto loser;
1558 }
1559
1560 status = SecCmsAttributeAddValue(poolp, attr, &attrValue);
1561
1562 loser:
1563 if (tmppoolp) {
1564 PORT_FreeArena(tmppoolp, PR_FALSE);
1565 }
1566 return status;
1567 }
1568
1569 /*!
1570 @function
1571 @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
1572 @discussion This is expected to be included in outgoing Apple code signatures.
1573 */
1574 OSStatus
1575 SecCmsSignerInfoAddAppleCodesigningHashAgilityV2(SecCmsSignerInfoRef signerinfo, CFDictionaryRef attrValues)
1576 {
1577 __block SecCmsAttribute *attr;
1578 __block PLArenaPool *poolp = signerinfo->cmsg->poolp;
1579 void *mark = PORT_ArenaMark(poolp);
1580 OSStatus status = SECFailure;
1581
1582 /* The value is required for this attribute. */
1583 if (!attrValues) {
1584 status = errSecParam;
1585 goto loser;
1586 }
1587
1588 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_APPLE_HASH_AGILITY_V2,
1589 NULL, PR_TRUE)) == NULL) {
1590 status = errSecAllocate;
1591 goto loser;
1592 }
1593
1594 CFDictionaryForEach(attrValues, ^(const void *key, const void *value) {
1595 if (!isNumber(key) || !isData(value)) {
1596 return;
1597 }
1598 (void)CMSAddAgileHashToAttribute(poolp, attr, (CFNumberRef)key, (CFDataRef)value);
1599 });
1600
1601 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
1602 status = errSecInternal;
1603 goto loser;
1604 }
1605
1606 PORT_ArenaUnmark(poolp, mark);
1607 return SECSuccess;
1608
1609 loser:
1610 PORT_ArenaRelease(poolp, mark);
1611 return status;
1612 }
1613
1614 /*
1615 * SecCmsSignerInfoAddAppleExpirationTime - add the expiration time to the
1616 * authenticated (i.e. signed) attributes of "signerinfo".
1617 *
1618 * This is expected to be included in outgoing signed
1619 * messages for Asset Receipts but is likely useful in other situations.
1620 *
1621 * This should only be added once; a second call will do nothing.
1622 */
1623 OSStatus
1624 SecCmsSignerInfoAddAppleExpirationTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t)
1625 {
1626 SecCmsAttribute *attr = NULL;
1627 PLArenaPool *poolp = signerinfo->cmsg->poolp;
1628 void *mark = PORT_ArenaMark(poolp);
1629
1630 /* create new expiration time attribute */
1631 SecAsn1Item etime;
1632 if (DER_CFDateToUTCTime(t, &etime) != SECSuccess) {
1633 goto loser;
1634 }
1635
1636 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_APPLE_EXPIRATION_TIME, &etime, PR_FALSE)) == NULL) {
1637 SECITEM_FreeItem (&etime, PR_FALSE);
1638 goto loser;
1639 }
1640
1641 SECITEM_FreeItem(&etime, PR_FALSE);
1642
1643 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
1644 goto loser;
1645 }
1646
1647 PORT_ArenaUnmark(poolp, mark);
1648 return SECSuccess;
1649
1650 loser:
1651 PORT_ArenaRelease(poolp, mark);
1652 return SECFailure;
1653 }
1654
1655 SecCertificateRef SecCmsSignerInfoCopyCertFromEncryptionKeyPreference(SecCmsSignerInfoRef signerinfo) {
1656 SecCertificateRef cert = NULL;
1657 SecCmsAttribute *attr;
1658 CSSM_DATA_PTR ekp;
1659 SecKeychainRef keychainOrArray;
1660
1661 (void)SecKeychainCopyDefault(&keychainOrArray);
1662
1663 /* sanity check - see if verification status is ok (unverified does not count...) */
1664 if (signerinfo->verificationStatus != SecCmsVSGoodSignature)
1665 return NULL;
1666
1667 /* Prep the raw certs */
1668 CSSM_DATA_PTR *rawCerts = NULL;
1669 if (signerinfo->sigd) {
1670 rawCerts = signerinfo->sigd->rawCerts;
1671 }
1672
1673 /* find preferred encryption cert */
1674 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) &&
1675 (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1676 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
1677 { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! Find the cert. */
1678 ekp = SecCmsAttributeGetValue(attr);
1679 if (ekp == NULL)
1680 return NULL;
1681 cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, rawCerts, ekp);
1682 }
1683 if(cert) return cert;
1684
1685 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) &&
1686 (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1687 SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
1688 { /* we have a MS_SMIME_ENCRYPTION_KEY_PREFERENCE attribute! Find the cert. */
1689 ekp = SecCmsAttributeGetValue(attr);
1690 if (ekp == NULL)
1691 return NULL;
1692 cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, rawCerts, ekp);
1693 }
1694 return cert;
1695 }
1696
1697 /*
1698 * XXXX the following needs to be done in the S/MIME layer code
1699 * after signature of a signerinfo is verified
1700 */
1701 OSStatus
1702 SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo)
1703 {
1704 SecCertificateRef cert = NULL;
1705 CSSM_DATA_PTR profile = NULL;
1706 SecCmsAttribute *attr;
1707 CSSM_DATA_PTR utc_stime = NULL;
1708 CSSM_DATA_PTR ekp;
1709 int save_error;
1710 OSStatus rv;
1711 Boolean must_free_cert = PR_FALSE;
1712 OSStatus status;
1713 SecKeychainRef keychainOrArray;
1714
1715 status = SecKeychainCopyDefault(&keychainOrArray);
1716
1717 /* sanity check - see if verification status is ok (unverified does not count...) */
1718 if (signerinfo->verificationStatus != SecCmsVSGoodSignature)
1719 return SECFailure;
1720
1721 /* find preferred encryption cert */
1722 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) &&
1723 (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1724 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
1725 { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
1726 ekp = SecCmsAttributeGetValue(attr);
1727 if (ekp == NULL)
1728 return SECFailure;
1729
1730 /* we assume that all certs coming with the message have been imported to the */
1731 /* temporary database */
1732 cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, NULL, ekp);
1733 if (cert == NULL)
1734 return SECFailure;
1735 must_free_cert = PR_TRUE;
1736 }
1737
1738 if (cert == NULL) {
1739 /* no preferred cert found?
1740 * find the cert the signerinfo is signed with instead */
1741 CFStringRef emailAddress=NULL;
1742
1743 cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray);
1744 if (cert == NULL)
1745 return SECFailure;
1746 if (SecCertificateGetEmailAddress(cert,&emailAddress))
1747 return SECFailure;
1748 }
1749
1750 /* verify this cert for encryption (has been verified for signing so far) */ /* don't verify this cert for encryption. It may just be a signing cert.
1751 * that's OK, we can still save the S/MIME profile. The encryption cert
1752 * should have already been saved */
1753 #ifdef notdef
1754 if (CERT_VerifyCert(keychainOrArray, cert, certUsageEmailRecipient, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1755 if (must_free_cert)
1756 CERT_DestroyCertificate(cert);
1757 return SECFailure;
1758 }
1759 #endif
1760
1761 /* XXX store encryption cert permanently? */
1762
1763 /*
1764 * Remember the current error set because we do not care about
1765 * anything set by the functions we are about to call.
1766 */
1767 save_error = PORT_GetError();
1768
1769 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
1770 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1771 SEC_OID_PKCS9_SMIME_CAPABILITIES,
1772 PR_TRUE);
1773 profile = SecCmsAttributeGetValue(attr);
1774 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1775 SEC_OID_PKCS9_SIGNING_TIME,
1776 PR_TRUE);
1777 utc_stime = SecCmsAttributeGetValue(attr);
1778 }
1779
1780 rv = CERT_SaveSMimeProfile (cert, profile, utc_stime);
1781 if (must_free_cert)
1782 CERT_DestroyCertificate(cert);
1783
1784 /*
1785 * Restore the saved error in case the calls above set a new
1786 * one that we do not actually care about.
1787 */
1788 PORT_SetError (save_error);
1789
1790 return rv;
1791 }
1792
1793 /*
1794 * SecCmsSignerInfoIncludeCerts - set cert chain inclusion mode for this signer
1795 */
1796 OSStatus
1797 SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo, SecCmsCertChainMode cm, SECCertUsage usage)
1798 {
1799 if (signerinfo->cert == NULL) {
1800 return SECFailure;
1801 }
1802
1803 /* don't leak if we get called twice */
1804 if (signerinfo->certList != NULL) {
1805 CFRelease(signerinfo->certList);
1806 signerinfo->certList = NULL;
1807 }
1808
1809 switch (cm) {
1810 case SecCmsCMNone:
1811 signerinfo->certList = NULL;
1812 break;
1813 case SecCmsCMCertOnly:
1814 signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
1815 break;
1816 case SecCmsCMCertChain:
1817 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE, PR_FALSE);
1818 break;
1819 case SecCmsCMCertChainWithRoot:
1820 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE, PR_FALSE);
1821 break;
1822 case SecCmsCMCertChainWithRootOrFail:
1823 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE, PR_TRUE);
1824 }
1825
1826 if (cm != SecCmsCMNone && signerinfo->certList == NULL) {
1827 return SECFailure;
1828 }
1829
1830 return SECSuccess;
1831 }