]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_smime/lib/cmssiginfo.c
Security-59306.101.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 if(pubkAlgTag == SEC_OID_EC_PUBLIC_KEY) {
460 /*
461 * RFC 3278 section section 2.1.1 states that the signatureAlgorithm
462 * field contains the full ecdsa-with-SHA1 OID, not plain old ecPublicKey
463 * as would appear in other forms of signed datas. However Microsoft doesn't
464 * do this, it puts ecPublicKey there, and if we put ecdsa-with-SHA1 there,
465 * MS can't verify - presumably because it takes the digest of the digest
466 * before feeding it to ECDSA.
467 * We handle this with a preference; default if it's not there is
468 * "Microsoft compatibility mode".
469 */
470 if(!SecCmsMsEcdsaCompatMode()) {
471 pubkAlgTag = SEC_OID_ECDSA_WithSHA1;
472 }
473 /* else violating the spec for compatibility */
474 }
475
476 if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag,
477 NULL) != SECSuccess)
478 goto loser;
479
480 return SECSuccess;
481
482 loser:
483 if (signature.Length != 0)
484 SECITEM_FreeItem (&signature, PR_FALSE);
485 if (privkey)
486 SECKEY_DestroyPrivateKey(privkey);
487 if (tmppoolp)
488 PORT_FreeArena(tmppoolp, PR_FALSE);
489 return SECFailure;
490 }
491
492 OSStatus
493 SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray,
494 CFTypeRef policies, SecTrustRef *trustRef)
495 {
496 SecCertificateRef cert;
497 CFAbsoluteTime stime;
498 OSStatus rv;
499 CSSM_DATA_PTR *otherCerts;
500
501 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray)) == NULL) {
502 dprintf("SecCmsSignerInfoVerifyCertificate: no signing cert\n");
503 signerinfo->verificationStatus = SecCmsVSSigningCertNotFound;
504 return SECFailure;
505 }
506
507 /*
508 * Get and convert the signing time; if available, it will be used
509 * both on the cert verification and for importing the sender
510 * email profile.
511 */
512 CFTypeRef timeStampPolicies=SecPolicyCreateAppleTimeStampingAndRevocationPolicies(policies);
513 if (SecCmsSignerInfoGetTimestampTimeWithPolicy(signerinfo, timeStampPolicies, &stime) != SECSuccess)
514 if (SecCmsSignerInfoGetSigningTime(signerinfo, &stime) != SECSuccess)
515 stime = CFAbsoluteTimeGetCurrent();
516 CFReleaseSafe(timeStampPolicies);
517
518 rv = SecCmsSignedDataRawCerts(signerinfo->sigd, &otherCerts);
519 if(rv) {
520 return rv;
521 }
522 rv = CERT_VerifyCert(keychainOrArray, cert, otherCerts, policies, stime, trustRef);
523 dprintfRC("SecCmsSignerInfoVerifyCertificate after vfy: certp %p cert.rc %d\n",
524 cert, (int)CFGetRetainCount(cert));
525 if (rv || !trustRef)
526 {
527 if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT)
528 {
529 /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */
530 if (signerinfo->verificationStatus == SecCmsVSGoodSignature)
531 signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted;
532 }
533 }
534 /* FIXME isn't this leaking the cert? */
535 dprintf("SecCmsSignerInfoVerifyCertificate: CertVerify rtn %d\n", (int)rv);
536 return rv;
537 }
538
539 static void debugShowSigningCertificate(SecCmsSignerInfoRef signerinfo)
540 {
541 #if SIGINFO_DEBUG
542 CFStringRef cn = SecCmsSignerInfoGetSignerCommonName(signerinfo);
543 if (cn)
544 {
545 char *ccn = cfStringToChar(cn);
546 if (ccn)
547 {
548 dprintf("SecCmsSignerInfoVerify: cn: %s\n", ccn);
549 free(ccn);
550 }
551 CFRelease(cn);
552 }
553 #endif
554 }
555
556 /*
557 * SecCmsSignerInfoVerify - verify the signature of a single SignerInfo
558 *
559 * Just verifies the signature. The assumption is that verification of the certificate
560 * is done already.
561 */
562 OSStatus
563 SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType)
564 {
565 return SecCmsSignerInfoVerifyWithPolicy(signerinfo,NULL, digest,contentType);
566 }
567
568 OSStatus
569 SecCmsSignerInfoVerifyWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType)
570 {
571 SecPublicKeyRef publickey = NULL;
572 SecCmsAttribute *attr = NULL;
573 CSSM_DATA encoded_attrs;
574 SecCertificateRef cert = NULL;
575 SecCmsVerificationStatus vs = SecCmsVSUnverified;
576 PLArenaPool *poolp = NULL;
577 SECOidTag digestAlgTag, digestEncAlgTag;
578
579 if (signerinfo == NULL)
580 return SECFailure;
581
582 /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */
583 /* cert has not been verified */
584 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL)) == NULL) {
585 dprintf("SecCmsSignerInfoVerify: no signing cert\n");
586 vs = SecCmsVSSigningCertNotFound;
587 goto loser;
588 }
589
590 dprintfRC("SecCmsSignerInfoVerify top: cert %p cert.rc %d\n", cert, (int)CFGetRetainCount(cert));
591
592 debugShowSigningCertificate(signerinfo);
593
594 if (NULL == (publickey = SecCertificateCopyKey(cert))) {
595 vs = SecCmsVSProcessingError;
596 goto loser;
597 }
598
599 digestAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg));
600 digestEncAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
601
602 /*
603 * Gross hack necessitated by RFC 3278 section 2.1.1, which states
604 * that the signature algorithm (here, digestEncAlg) contains ecdsa_with-SHA1,
605 * *not* (as in all other algorithms) the raw signature algorithm, e.g.
606 * pkcs1RSAEncryption.
607 */
608 if(digestEncAlgTag == SEC_OID_ECDSA_WithSHA1) {
609 digestEncAlgTag = SEC_OID_EC_PUBLIC_KEY;
610 }
611
612 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
613 if (contentType) {
614 /*
615 * Check content type
616 *
617 * RFC2630 sez that if there are any authenticated attributes,
618 * then there must be one for content type which matches the
619 * content type of the content being signed, and there must
620 * be one for message digest which matches our message digest.
621 * So check these things first.
622 */
623 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
624 SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE)) == NULL)
625 {
626 vs = SecCmsVSMalformedSignature;
627 goto loser;
628 }
629
630 if (SecCmsAttributeCompareValue(attr, contentType) == PR_FALSE) {
631 vs = SecCmsVSMalformedSignature;
632 goto loser;
633 }
634 }
635
636 /*
637 * Check digest
638 */
639 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE)) == NULL)
640 {
641 vs = SecCmsVSMalformedSignature;
642 goto loser;
643 }
644 if (SecCmsAttributeCompareValue(attr, digest) == PR_FALSE) {
645 vs = SecCmsVSDigestMismatch;
646 goto loser;
647 }
648
649 if ((poolp = PORT_NewArena (1024)) == NULL) {
650 vs = SecCmsVSProcessingError;
651 goto loser;
652 }
653
654 /*
655 * Check signature
656 *
657 * The signature is based on a digest of the DER-encoded authenticated
658 * attributes. So, first we encode and then we digest/verify.
659 * we trust the decoder to have the attributes in the right (sorted) order
660 */
661 encoded_attrs.Data = NULL;
662 encoded_attrs.Length = 0;
663
664 if (SecCmsAttributeArrayEncode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL ||
665 encoded_attrs.Data == NULL || encoded_attrs.Length == 0)
666 {
667 vs = SecCmsVSProcessingError;
668 goto loser;
669 }
670
671 vs = (VFY_VerifyData (encoded_attrs.Data, (int)encoded_attrs.Length,
672 publickey, &(signerinfo->encDigest),
673 digestAlgTag, digestEncAlgTag,
674 signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature;
675
676 dprintf("VFY_VerifyData (authenticated attributes): %s\n",
677 (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature");
678
679 PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */
680
681 } else {
682 CSSM_DATA_PTR sig;
683
684 /* No authenticated attributes. The signature is based on the plain message digest. */
685 sig = &(signerinfo->encDigest);
686 if (sig->Length == 0)
687 goto loser;
688
689 vs = (VFY_VerifyDigest(digest, publickey, sig,
690 digestAlgTag, digestEncAlgTag,
691 signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature;
692
693 dprintf("VFY_VerifyData (plain message digest): %s\n",
694 (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature");
695 }
696
697 if (!SecCmsArrayIsEmpty((void **)signerinfo->unAuthAttr))
698 {
699 dprintf("found an unAuthAttr\n");
700 OSStatus rux = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo,timeStampPolicy);
701 dprintf("SecCmsSignerInfoVerifyUnAuthAttrs Status: %ld\n", (long)rux);
702 if (rux) {
703 goto loser;
704 }
705 }
706
707 if (vs == SecCmsVSBadSignature) {
708 /*
709 * XXX Change the generic error into our specific one, because
710 * in that case we get a better explanation out of the Security
711 * Advisor. This is really a bug in our error strings (the
712 * "generic" error has a lousy/wrong message associated with it
713 * which assumes the signature verification was done for the
714 * purposes of checking the issuer signature on a certificate)
715 * but this is at least an easy workaround and/or in the
716 * Security Advisor, which specifically checks for the error
717 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
718 * in that case but does not similarly check for
719 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would
720 * probably say the wrong thing in the case that it *was* the
721 * certificate signature check that failed during the cert
722 * verification done above. Our error handling is really a mess.
723 */
724 if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
725 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
726 }
727
728 if (publickey != NULL)
729 CFRelease(publickey);
730
731 signerinfo->verificationStatus = vs;
732 dprintfRC("SecCmsSignerInfoVerify end: cerp %p cert.rc %d\n",
733 cert, (int)CFGetRetainCount(cert));
734
735 dprintf("verificationStatus: %d\n", vs);
736
737 return (vs == SecCmsVSGoodSignature) ? SECSuccess : SECFailure;
738
739 loser:
740 if (publickey != NULL)
741 SECKEY_DestroyPublicKey (publickey);
742
743 dprintf("verificationStatus2: %d\n", vs);
744 signerinfo->verificationStatus = vs;
745
746 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
747 return SECFailure;
748 }
749
750 OSStatus
751 SecCmsSignerInfoVerifyUnAuthAttrs(SecCmsSignerInfoRef signerinfo) {
752 return SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo, NULL);
753 }
754
755 OSStatus
756 SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy)
757 {
758 /*
759 unAuthAttr is an array of attributes; we expect to
760 see just one: the timestamp blob. If we have an unAuthAttr,
761 but don't see a timestamp, return an error since we have
762 no other cases where this would be present.
763 */
764
765 SecCmsAttribute *attr = NULL;
766 OSStatus status = SECFailure;
767
768 require(signerinfo, xit);
769 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->unAuthAttr,
770 SEC_OID_PKCS9_TIMESTAMP_TOKEN, PR_TRUE);
771 if (attr == NULL)
772 {
773 status = errSecTimestampMissing;
774 goto xit;
775 }
776
777 dprintf("found an id-ct-TSTInfo\n");
778 // Don't check the nonce in this case
779 status = decodeTimeStampTokenWithPolicy(signerinfo, timeStampPolicy, (attr->values)[0], &signerinfo->encDigest, 0);
780 if (status != errSecSuccess) {
781 secerror("timestamp verification failed: %d", (int)status);
782 }
783
784 xit:
785 return status;
786 }
787
788 CSSM_DATA *
789 SecCmsSignerInfoGetEncDigest(SecCmsSignerInfoRef signerinfo)
790 {
791 return &signerinfo->encDigest;
792 }
793
794 SecCmsVerificationStatus
795 SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo)
796 {
797 return signerinfo->verificationStatus;
798 }
799
800 SECOidData *
801 SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo)
802 {
803 return SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
804 }
805
806 SECOidTag
807 SecCmsSignerInfoGetDigestAlgTag(SecCmsSignerInfoRef signerinfo)
808 {
809 SECOidData *algdata;
810
811 algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
812 if (algdata != NULL)
813 return algdata->offset;
814 else
815 return SEC_OID_UNKNOWN;
816 }
817
818 CFArrayRef
819 SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo)
820 {
821 dprintfRC("SecCmsSignerInfoGetCertList: certList.rc %d\n",
822 (int)CFGetRetainCount(signerinfo->certList));
823 return signerinfo->certList;
824 }
825
826 CFArrayRef
827 SecCmsSignerInfoGetTimestampCertList(SecCmsSignerInfoRef signerinfo)
828 {
829 dprintfRC("SecCmsSignerInfoGetTimestampCertList: timestampCertList.rc %d\n",
830 (int)CFGetRetainCount(signerinfo->timestampCertList));
831 return signerinfo->timestampCertList;
832 }
833
834 SecCertificateRef
835 SecCmsSignerInfoGetTimestampSigningCert(SecCmsSignerInfoRef signerinfo)
836 {
837 dprintfRC("SecCmsSignerInfoGetTimestampSigningCert: timestampCert.rc %d\n",
838 (int)CFGetRetainCount(signerinfo->timestampCert));
839 return signerinfo->timestampCert;
840 }
841
842 int
843 SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo)
844 {
845 unsigned long version;
846
847 /* always take apart the CSSM_DATA */
848 if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
849 return 0;
850 else
851 return (int)version;
852 }
853
854 /*
855 * SecCmsSignerInfoGetSigningTime - return the signing time,
856 * in UTCTime format, of a CMS signerInfo.
857 *
858 * sinfo - signerInfo data for this signer
859 *
860 * Returns a pointer to XXXX (what?)
861 * A return value of NULL is an error.
862 */
863 OSStatus
864 SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime)
865 {
866 SecCmsAttribute *attr;
867 CSSM_DATA_PTR value;
868
869 if (sinfo == NULL)
870 return paramErr;
871
872 if (sinfo->signingTime != 0) {
873 *stime = sinfo->signingTime; /* cached copy */
874 return SECSuccess;
875 }
876
877 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
878 /* XXXX multi-valued attributes NIH */
879 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
880 return errSecSigningTimeMissing;
881 if (DER_UTCTimeToCFDate(value, stime) != SECSuccess)
882 return errSecSigningTimeMissing;
883 sinfo->signingTime = *stime; /* make cached copy */
884 return SECSuccess;
885 }
886
887 OSStatus
888 SecCmsSignerInfoGetTimestampTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime)
889 {
890 return SecCmsSignerInfoGetTimestampTimeWithPolicy(sinfo, NULL, stime);
891 }
892
893 OSStatus
894 SecCmsSignerInfoGetTimestampTimeWithPolicy(SecCmsSignerInfoRef sinfo, CFTypeRef timeStampPolicy, CFAbsoluteTime *stime)
895 {
896 OSStatus status = paramErr;
897
898 require(sinfo && stime, xit);
899
900 if (sinfo->timestampTime != 0)
901 {
902 *stime = sinfo->timestampTime; /* cached copy */
903 return noErr;
904 }
905
906 // A bit heavyweight if haven't already called verify
907 status = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(sinfo,timeStampPolicy);
908 *stime = sinfo->timestampTime;
909 xit:
910 return status;
911 }
912
913 /*!
914 @function
915 @abstract Return the data in the signed Codesigning Hash Agility attribute.
916 @param sinfo SignerInfo data for this signer, pointer to a CFDataRef for attribute value
917 @discussion Returns a CFDataRef containing the value of the attribute
918 @result A return value of errSecInternal is an error trying to look up the oid.
919 A status value of success with null result data indicates the attribute was not present.
920 */
921 OSStatus
922 SecCmsSignerInfoGetAppleCodesigningHashAgility(SecCmsSignerInfoRef sinfo, CFDataRef *sdata)
923 {
924 SecCmsAttribute *attr;
925 CSSM_DATA_PTR value;
926
927 if (sinfo == NULL || sdata == NULL)
928 return paramErr;
929
930 *sdata = NULL;
931
932 if (sinfo->hashAgilityAttrValue != NULL) {
933 *sdata = sinfo->hashAgilityAttrValue; /* cached copy */
934 return SECSuccess;
935 }
936
937 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_HASH_AGILITY, PR_TRUE);
938
939 /* attribute not found */
940 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
941 return SECSuccess;
942
943 sinfo->hashAgilityAttrValue = CFDataCreate(NULL, value->Data, value->Length); /* make cached copy */
944 if (sinfo->hashAgilityAttrValue) {
945 *sdata = sinfo->hashAgilityAttrValue;
946 return SECSuccess;
947 }
948 return errSecAllocate;
949 }
950
951 /* AgileHash ::= SEQUENCE {
952 hashType OBJECT IDENTIFIER,
953 hashValues OCTET STRING }
954 */
955 typedef struct {
956 SecAsn1Item digestOID;
957 SecAsn1Item digestValue;
958 } CMSAppleAgileHash;
959
960 static const SecAsn1Template CMSAppleAgileHashTemplate[] = {
961 { SEC_ASN1_SEQUENCE,
962 0, NULL, sizeof(CMSAppleAgileHash) },
963 { SEC_ASN1_OBJECT_ID,
964 offsetof(CMSAppleAgileHash, digestOID), },
965 { SEC_ASN1_OCTET_STRING,
966 offsetof(CMSAppleAgileHash, digestValue), },
967 { 0, }
968 };
969
970 static OSStatus CMSAddAgileHashToDictionary(CFMutableDictionaryRef dictionary, SecAsn1Item *DERAgileHash) {
971 PLArenaPool *tmppoolp = NULL;
972 OSStatus status = errSecSuccess;
973 CMSAppleAgileHash agileHash;
974 CFDataRef digestValue = NULL;
975 CFNumberRef digestTag = NULL;
976
977 tmppoolp = PORT_NewArena(1024);
978 if (tmppoolp == NULL) {
979 return errSecAllocate;
980 }
981
982 if ((status = SEC_ASN1DecodeItem(tmppoolp, &agileHash, CMSAppleAgileHashTemplate, DERAgileHash)) != errSecSuccess) {
983 goto loser;
984 }
985
986 int64_t tag = SECOID_FindOIDTag(&agileHash.digestOID);
987 digestTag = CFNumberCreate(NULL, kCFNumberSInt64Type, &tag);
988 digestValue = CFDataCreate(NULL, agileHash.digestValue.Data, agileHash.digestValue.Length);
989 CFDictionaryAddValue(dictionary, digestTag, digestValue);
990
991 loser:
992 CFReleaseNull(digestValue);
993 CFReleaseNull(digestTag);
994 if (tmppoolp) {
995 PORT_FreeArena(tmppoolp, PR_FALSE);
996 }
997 return status;
998 }
999
1000 /*!
1001 @function
1002 @abstract Return the data in the signed Codesigning Hash Agility V2 attribute.
1003 @param sinfo SignerInfo data for this signer, pointer to a CFDictionaryRef for attribute values
1004 @discussion Returns a CFDictionaryRef containing the values of the attribute
1005 @result A return value of errSecInternal is an error trying to look up the oid.
1006 A status value of success with null result data indicates the attribute was not present.
1007 */
1008 OSStatus
1009 SecCmsSignerInfoGetAppleCodesigningHashAgilityV2(SecCmsSignerInfoRef sinfo, CFDictionaryRef *sdict)
1010 {
1011 SecCmsAttribute *attr;
1012
1013 if (sinfo == NULL || sdict == NULL) {
1014 return errSecParam;
1015 }
1016
1017 *sdict = NULL;
1018
1019 if (sinfo->hashAgilityV2AttrValues != NULL) {
1020 *sdict = sinfo->hashAgilityV2AttrValues; /* cached copy */
1021 return SECSuccess;
1022 }
1023
1024 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_HASH_AGILITY_V2, PR_TRUE);
1025
1026 /* attribute not found */
1027 if (attr == NULL) {
1028 return SECSuccess;
1029 }
1030
1031 /* attrValues SET OF AttributeValue
1032 * AttributeValue ::= ANY
1033 */
1034 CSSM_DATA_PTR *values = attr->values;
1035 if (values == NULL) { /* There must be values */
1036 return errSecDecode;
1037 }
1038
1039 CFMutableDictionaryRef agileHashValues = CFDictionaryCreateMutable(NULL, SecCmsArrayCount((void **)values),
1040 &kCFTypeDictionaryKeyCallBacks,
1041 &kCFTypeDictionaryValueCallBacks);
1042 while (*values != NULL) {
1043 (void)CMSAddAgileHashToDictionary(agileHashValues, *values++);
1044 }
1045 if (CFDictionaryGetCount(agileHashValues) != SecCmsArrayCount((void **)attr->values)) {
1046 CFReleaseNull(agileHashValues);
1047 return errSecDecode;
1048 }
1049
1050 sinfo->hashAgilityV2AttrValues = agileHashValues; /* make cached copy */
1051 if (sinfo->hashAgilityV2AttrValues) {
1052 *sdict = sinfo->hashAgilityV2AttrValues;
1053 return SECSuccess;
1054 }
1055 return errSecAllocate;
1056 }
1057
1058 /*
1059 * SecCmsSignerInfoGetAppleExpirationTime - return the expiration time,
1060 * in UTCTime format, of a CMS signerInfo.
1061 *
1062 * sinfo - signerInfo data for this signer
1063 *
1064 * Returns a pointer to XXXX (what?)
1065 * A return value of NULL is an error.
1066 */
1067 OSStatus
1068 SecCmsSignerInfoGetAppleExpirationTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *etime)
1069 {
1070 SecCmsAttribute *attr = NULL;
1071 SecAsn1Item * value = NULL;
1072
1073 if (sinfo == NULL || etime == NULL) {
1074 return SECFailure;
1075 }
1076
1077 if (sinfo->expirationTime != 0) {
1078 *etime = sinfo->expirationTime; /* cached copy */
1079 return SECSuccess;
1080 }
1081
1082 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_EXPIRATION_TIME, PR_TRUE);
1083 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL) {
1084 return SECFailure;
1085 }
1086 if (DER_UTCTimeToCFDate(value, etime) != SECSuccess) {
1087 return SECFailure;
1088 }
1089 sinfo->expirationTime = *etime; /* make cached copy */
1090 return SECSuccess;
1091 }
1092
1093 /*
1094 * Return the signing cert of a CMS signerInfo.
1095 *
1096 * the certs in the enclosing SignedData must have been imported already
1097 */
1098 SecCertificateRef
1099 SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray)
1100 {
1101 SecCertificateRef cert;
1102 SecCmsSignerIdentifier *sid;
1103 OSStatus ortn;
1104 CSSM_DATA_PTR *rawCerts;
1105
1106 if (signerinfo->cert != NULL) {
1107 dprintfRC("SecCmsSignerInfoGetSigningCertificate top: cert %p cert.rc %d\n",
1108 signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert));
1109 return signerinfo->cert;
1110 }
1111 ortn = SecCmsSignedDataRawCerts(signerinfo->sigd, &rawCerts);
1112 if(ortn) {
1113 return NULL;
1114 }
1115 dprintf("SecCmsSignerInfoGetSigningCertificate: numRawCerts %d\n",
1116 SecCmsArrayCount((void **)rawCerts));
1117
1118 /*
1119 * This cert will also need to be freed, but since we save it
1120 * in signerinfo for later, we do not want to destroy it when
1121 * we leave this function -- we let the clean-up of the entire
1122 * cinfo structure later do the destroy of this cert.
1123 */
1124 sid = &signerinfo->signerIdentifier;
1125 switch (sid->identifierType) {
1126 case SecCmsSignerIDIssuerSN:
1127 cert = CERT_FindCertByIssuerAndSN(keychainOrArray, rawCerts, signerinfo->sigd->certs, signerinfo->cmsg->poolp,
1128 sid->id.issuerAndSN);
1129 break;
1130 case SecCmsSignerIDSubjectKeyID:
1131 cert = CERT_FindCertBySubjectKeyID(keychainOrArray, rawCerts, signerinfo->sigd->certs, sid->id.subjectKeyID);
1132 break;
1133 default:
1134 cert = NULL;
1135 break;
1136 }
1137
1138 /* cert can be NULL at that point */
1139 signerinfo->cert = cert; /* earmark it */
1140 dprintfRC("SecCmsSignerInfoGetSigningCertificate end: certp %p cert.rc %d\n",
1141 signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert));
1142
1143 return cert;
1144 }
1145
1146 /*
1147 * SecCmsSignerInfoGetSignerCommonName - return the common name of the signer
1148 *
1149 * sinfo - signerInfo data for this signer
1150 *
1151 * Returns a CFStringRef containing the common name of the signer.
1152 * A return value of NULL is an error.
1153 */
1154 CFStringRef
1155 SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo)
1156 {
1157 SecCertificateRef signercert;
1158 CFStringRef commonName = NULL;
1159
1160 /* will fail if cert is not verified */
1161 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
1162 return NULL;
1163
1164 if (errSecSuccess != SecCertificateCopyCommonName(signercert, &commonName)) {
1165 return NULL;
1166 }
1167
1168 return commonName;
1169 }
1170
1171 /*
1172 * SecCmsSignerInfoGetSignerEmailAddress - return the email address of the signer
1173 *
1174 * sinfo - signerInfo data for this signer
1175 *
1176 * Returns a CFStringRef containing the name of the signer.
1177 * A return value of NULL is an error.
1178 */
1179 CFStringRef
1180 SecCmsSignerInfoGetSignerEmailAddress(SecCmsSignerInfoRef sinfo)
1181 {
1182 SecCertificateRef signercert;
1183 CFStringRef emailAddress = NULL;
1184
1185 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
1186 return NULL;
1187
1188 SecCertificateGetEmailAddress(signercert, &emailAddress);
1189
1190 return CFRetainSafe(emailAddress);
1191 }
1192
1193
1194 /*
1195 * SecCmsSignerInfoAddAuthAttr - add an attribute to the
1196 * authenticated (i.e. signed) attributes of "signerinfo".
1197 */
1198 OSStatus
1199 SecCmsSignerInfoAddAuthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
1200 {
1201 return SecCmsAttributeArrayAddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
1202 }
1203
1204 /*
1205 * SecCmsSignerInfoAddUnauthAttr - add an attribute to the
1206 * unauthenticated attributes of "signerinfo".
1207 */
1208 OSStatus
1209 SecCmsSignerInfoAddUnauthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
1210 {
1211 return SecCmsAttributeArrayAddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
1212 }
1213
1214 /*
1215 * SecCmsSignerInfoAddSigningTime - add the signing time to the
1216 * authenticated (i.e. signed) attributes of "signerinfo".
1217 *
1218 * This is expected to be included in outgoing signed
1219 * messages for email (S/MIME) but is likely useful in other situations.
1220 *
1221 * This should only be added once; a second call will do nothing.
1222 *
1223 * XXX This will probably just shove the current time into "signerinfo"
1224 * but it will not actually get signed until the entire item is
1225 * processed for encoding. Is this (expected to be small) delay okay?
1226 */
1227 OSStatus
1228 SecCmsSignerInfoAddSigningTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t)
1229 {
1230 SecCmsAttribute *attr;
1231 CSSM_DATA stime;
1232 void *mark;
1233 PLArenaPool *poolp;
1234
1235 poolp = signerinfo->cmsg->poolp;
1236
1237 mark = PORT_ArenaMark(poolp);
1238
1239 /* create new signing time attribute */
1240 if (DER_CFDateToUTCTime(t, &stime) != SECSuccess)
1241 goto loser;
1242
1243 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
1244 SECITEM_FreeItem (&stime, PR_FALSE);
1245 goto loser;
1246 }
1247
1248 SECITEM_FreeItem (&stime, PR_FALSE);
1249
1250 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1251 goto loser;
1252
1253 PORT_ArenaUnmark (poolp, mark);
1254
1255 return SECSuccess;
1256
1257 loser:
1258 PORT_ArenaRelease (poolp, mark);
1259 return SECFailure;
1260 }
1261
1262 /*
1263 * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the
1264 * authenticated (i.e. signed) attributes of "signerinfo".
1265 *
1266 * This is expected to be included in outgoing signed
1267 * messages for email (S/MIME).
1268 */
1269 OSStatus
1270 SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo)
1271 {
1272 SecCmsAttribute *attr;
1273 CSSM_DATA_PTR smimecaps = NULL;
1274 void *mark;
1275 PLArenaPool *poolp;
1276
1277 poolp = signerinfo->cmsg->poolp;
1278
1279 mark = PORT_ArenaMark(poolp);
1280
1281 smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
1282 if (smimecaps == NULL)
1283 goto loser;
1284
1285 /* create new signing time attribute */
1286 #if 1
1287 // @@@ We don't do Fortezza yet.
1288 if (SecSMIMECreateSMIMECapabilities((SecArenaPoolRef)poolp, smimecaps, PR_FALSE) != SECSuccess)
1289 #else
1290 if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps,
1291 PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess)
1292 #endif
1293 goto loser;
1294
1295 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
1296 goto loser;
1297
1298 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1299 goto loser;
1300
1301 PORT_ArenaUnmark (poolp, mark);
1302 return SECSuccess;
1303
1304 loser:
1305 PORT_ArenaRelease (poolp, mark);
1306 return SECFailure;
1307 }
1308
1309 /*
1310 * SecCmsSignerInfoAddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1311 * authenticated (i.e. signed) attributes of "signerinfo".
1312 *
1313 * This is expected to be included in outgoing signed messages for email (S/MIME).
1314 */
1315 OSStatus
1316 SecCmsSignerInfoAddSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1317 {
1318 SecCmsAttribute *attr;
1319 CSSM_DATA_PTR smimeekp = NULL;
1320 void *mark;
1321 PLArenaPool *poolp;
1322
1323 #if 0
1324 CFTypeRef policy;
1325
1326 /* verify this cert for encryption */
1327 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1328 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1329 CFRelease(policy);
1330 return SECFailure;
1331 }
1332 CFRelease(policy);
1333 #endif
1334
1335 poolp = signerinfo->cmsg->poolp;
1336 mark = PORT_ArenaMark(poolp);
1337
1338 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1339 if (smimeekp == NULL)
1340 goto loser;
1341
1342 /* create new signing time attribute */
1343 if (SecSMIMECreateSMIMEEncKeyPrefs((SecArenaPoolRef)poolp, smimeekp, cert) != SECSuccess)
1344 goto loser;
1345
1346 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1347 goto loser;
1348
1349 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1350 goto loser;
1351
1352 PORT_ArenaUnmark (poolp, mark);
1353 return SECSuccess;
1354
1355 loser:
1356 PORT_ArenaRelease (poolp, mark);
1357 return SECFailure;
1358 }
1359
1360 /*
1361 * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1362 * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft.
1363 *
1364 * This is expected to be included in outgoing signed messages for email (S/MIME),
1365 * if compatibility with Microsoft mail clients is wanted.
1366 */
1367 OSStatus
1368 SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1369 {
1370 SecCmsAttribute *attr;
1371 CSSM_DATA_PTR smimeekp = NULL;
1372 void *mark;
1373 PLArenaPool *poolp;
1374
1375 #if 0
1376 CFTypeRef policy;
1377
1378 /* verify this cert for encryption */
1379 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1380 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1381 CFRelease(policy);
1382 return SECFailure;
1383 }
1384 CFRelease(policy);
1385 #endif
1386
1387 poolp = signerinfo->cmsg->poolp;
1388 mark = PORT_ArenaMark(poolp);
1389
1390 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1391 if (smimeekp == NULL)
1392 goto loser;
1393
1394 /* create new signing time attribute */
1395 if (SecSMIMECreateMSSMIMEEncKeyPrefs((SecArenaPoolRef)poolp, smimeekp, cert) != SECSuccess)
1396 goto loser;
1397
1398 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1399 goto loser;
1400
1401 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1402 goto loser;
1403
1404 PORT_ArenaUnmark (poolp, mark);
1405 return SECSuccess;
1406
1407 loser:
1408 PORT_ArenaRelease (poolp, mark);
1409 return SECFailure;
1410 }
1411
1412 /*
1413 * SecCmsSignerInfoAddTimeStamp - add time stamp to the
1414 * unauthenticated (i.e. unsigned) attributes of "signerinfo".
1415 *
1416 * This will initially be used for time stamping signed applications
1417 * by using a Time Stamping Authority. It may also be included in outgoing signed
1418 * messages for email (S/MIME), and may be useful in other situations.
1419 *
1420 * This should only be added once; a second call will do nothing.
1421 *
1422 */
1423
1424 /*
1425 Countersignature attribute values have ASN.1 type Countersignature:
1426 Countersignature ::= SignerInfo
1427 Countersignature values have the same meaning as SignerInfo values
1428 for ordinary signatures, except that:
1429 1. The signedAttributes field MUST NOT contain a content-type
1430 attribute; there is no content type for countersignatures.
1431 2. The signedAttributes field MUST contain a message-digest
1432 attribute if it contains any other attributes.
1433 3. The input to the message-digesting process is the contents octets
1434 of the DER encoding of the signatureValue field of the SignerInfo
1435 value with which the attribute is associated.
1436 */
1437
1438 /*!
1439 @function
1440 @abstract Create a timestamp unsigned attribute with a TimeStampToken.
1441 */
1442
1443 OSStatus
1444 SecCmsSignerInfoAddTimeStamp(SecCmsSignerInfoRef signerinfo, CSSM_DATA *tstoken)
1445 {
1446 SecCmsAttribute *attr;
1447 PLArenaPool *poolp = signerinfo->cmsg->poolp;
1448 void *mark = PORT_ArenaMark(poolp);
1449
1450 // We have already encoded this ourselves, so last param is PR_TRUE
1451 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_TIMESTAMP_TOKEN, tstoken, PR_TRUE)) == NULL)
1452 goto loser;
1453
1454 if (SecCmsSignerInfoAddUnauthAttr(signerinfo, attr) != SECSuccess)
1455 goto loser;
1456
1457 PORT_ArenaUnmark (poolp, mark);
1458
1459 return SECSuccess;
1460
1461 loser:
1462 PORT_ArenaRelease (poolp, mark);
1463 return SECFailure;
1464 }
1465
1466 /*
1467 * SecCmsSignerInfoAddCounterSignature - countersign a signerinfo
1468 *
1469 * 1. digest the DER-encoded signature value of the original signerinfo
1470 * 2. create new signerinfo with correct version, sid, digestAlg
1471 * 3. add message-digest authAttr, but NO content-type
1472 * 4. sign the authAttrs
1473 * 5. DER-encode the new signerInfo
1474 * 6. add the whole thing to original signerInfo's unAuthAttrs
1475 * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
1476 *
1477 * XXXX give back the new signerinfo?
1478 */
1479 OSStatus
1480 SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo,
1481 SECOidTag digestalg, SecIdentityRef identity)
1482 {
1483 /* XXXX TBD XXXX */
1484 return SECFailure;
1485 }
1486
1487 /*!
1488 @function
1489 @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
1490 @discussion This is expected to be included in outgoing Apple code signatures.
1491 */
1492 OSStatus
1493 SecCmsSignerInfoAddAppleCodesigningHashAgility(SecCmsSignerInfoRef signerinfo, CFDataRef attrValue)
1494 {
1495 SecCmsAttribute *attr;
1496 PLArenaPool *poolp = signerinfo->cmsg->poolp;
1497 void *mark = PORT_ArenaMark(poolp);
1498 OSStatus status = SECFailure;
1499
1500 /* The value is required for this attribute. */
1501 if (!attrValue) {
1502 status = errSecParam;
1503 goto loser;
1504 }
1505
1506 /*
1507 * SecCmsAttributeCreate makes a copy of the data in value, so
1508 * we don't need to copy into the CSSM_DATA struct.
1509 */
1510 CSSM_DATA value;
1511 value.Length = CFDataGetLength(attrValue);
1512 value.Data = (uint8_t *)CFDataGetBytePtr(attrValue);
1513
1514 if ((attr = SecCmsAttributeCreate(poolp,
1515 SEC_OID_APPLE_HASH_AGILITY,
1516 &value,
1517 PR_FALSE)) == NULL) {
1518 status = errSecAllocate;
1519 goto loser;
1520 }
1521
1522 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
1523 status = errSecInternalError;
1524 goto loser;
1525 }
1526
1527 PORT_ArenaUnmark(poolp, mark);
1528 return SECSuccess;
1529
1530 loser:
1531 PORT_ArenaRelease(poolp, mark);
1532 return status;
1533 }
1534
1535 static OSStatus CMSAddAgileHashToAttribute(PLArenaPool *poolp, SecCmsAttribute *attr, CFNumberRef cftag, CFDataRef value) {
1536 PLArenaPool *tmppoolp = NULL;
1537 int64_t tag;
1538 SECOidData *digestOid = NULL;
1539 CMSAppleAgileHash agileHash;
1540 SecAsn1Item attrValue = { .Data = NULL, .Length = 0 };
1541 OSStatus status = errSecSuccess;
1542
1543 memset(&agileHash, 0, sizeof(agileHash));
1544
1545 if(!CFNumberGetValue(cftag, kCFNumberSInt64Type, &tag)) {
1546 return errSecParam;
1547 }
1548 digestOid = SECOID_FindOIDByTag((SECOidTag)tag);
1549
1550 agileHash.digestValue.Data = (uint8_t *)CFDataGetBytePtr(value);
1551 agileHash.digestValue.Length = CFDataGetLength(value);
1552 agileHash.digestOID.Data = digestOid->oid.Data;
1553 agileHash.digestOID.Length = digestOid->oid.Length;
1554
1555 tmppoolp = PORT_NewArena(1024);
1556 if (tmppoolp == NULL) {
1557 return errSecAllocate;
1558 }
1559
1560 if (SEC_ASN1EncodeItem(tmppoolp, &attrValue, &agileHash, CMSAppleAgileHashTemplate) == NULL) {
1561 status = errSecParam;
1562 goto loser;
1563 }
1564
1565 status = SecCmsAttributeAddValue(poolp, attr, &attrValue);
1566
1567 loser:
1568 if (tmppoolp) {
1569 PORT_FreeArena(tmppoolp, PR_FALSE);
1570 }
1571 return status;
1572 }
1573
1574 /*!
1575 @function
1576 @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
1577 @discussion This is expected to be included in outgoing Apple code signatures.
1578 */
1579 OSStatus
1580 SecCmsSignerInfoAddAppleCodesigningHashAgilityV2(SecCmsSignerInfoRef signerinfo, CFDictionaryRef attrValues)
1581 {
1582 __block SecCmsAttribute *attr;
1583 __block PLArenaPool *poolp = signerinfo->cmsg->poolp;
1584 void *mark = PORT_ArenaMark(poolp);
1585 OSStatus status = SECFailure;
1586
1587 /* The value is required for this attribute. */
1588 if (!attrValues) {
1589 status = errSecParam;
1590 goto loser;
1591 }
1592
1593 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_APPLE_HASH_AGILITY_V2,
1594 NULL, PR_TRUE)) == NULL) {
1595 status = errSecAllocate;
1596 goto loser;
1597 }
1598
1599 CFDictionaryForEach(attrValues, ^(const void *key, const void *value) {
1600 if (!isNumber(key) || !isData(value)) {
1601 return;
1602 }
1603 (void)CMSAddAgileHashToAttribute(poolp, attr, (CFNumberRef)key, (CFDataRef)value);
1604 });
1605
1606 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
1607 status = errSecInternal;
1608 goto loser;
1609 }
1610
1611 PORT_ArenaUnmark(poolp, mark);
1612 return SECSuccess;
1613
1614 loser:
1615 PORT_ArenaRelease(poolp, mark);
1616 return status;
1617 }
1618
1619 /*
1620 * SecCmsSignerInfoAddAppleExpirationTime - add the expiration time to the
1621 * authenticated (i.e. signed) attributes of "signerinfo".
1622 *
1623 * This is expected to be included in outgoing signed
1624 * messages for Asset Receipts but is likely useful in other situations.
1625 *
1626 * This should only be added once; a second call will do nothing.
1627 */
1628 OSStatus
1629 SecCmsSignerInfoAddAppleExpirationTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t)
1630 {
1631 SecCmsAttribute *attr = NULL;
1632 PLArenaPool *poolp = signerinfo->cmsg->poolp;
1633 void *mark = PORT_ArenaMark(poolp);
1634
1635 /* create new expiration time attribute */
1636 SecAsn1Item etime;
1637 if (DER_CFDateToUTCTime(t, &etime) != SECSuccess) {
1638 goto loser;
1639 }
1640
1641 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_APPLE_EXPIRATION_TIME, &etime, PR_FALSE)) == NULL) {
1642 SECITEM_FreeItem (&etime, PR_FALSE);
1643 goto loser;
1644 }
1645
1646 SECITEM_FreeItem(&etime, PR_FALSE);
1647
1648 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
1649 goto loser;
1650 }
1651
1652 PORT_ArenaUnmark(poolp, mark);
1653 return SECSuccess;
1654
1655 loser:
1656 PORT_ArenaRelease(poolp, mark);
1657 return SECFailure;
1658 }
1659
1660 SecCertificateRef SecCmsSignerInfoCopyCertFromEncryptionKeyPreference(SecCmsSignerInfoRef signerinfo) {
1661 SecCertificateRef cert = NULL;
1662 SecCmsAttribute *attr;
1663 CSSM_DATA_PTR ekp;
1664 SecKeychainRef keychainOrArray;
1665
1666 (void)SecKeychainCopyDefault(&keychainOrArray);
1667
1668 /* sanity check - see if verification status is ok (unverified does not count...) */
1669 if (signerinfo->verificationStatus != SecCmsVSGoodSignature)
1670 return NULL;
1671
1672 /* Prep the raw certs */
1673 CSSM_DATA_PTR *rawCerts = NULL;
1674 if (signerinfo->sigd) {
1675 rawCerts = signerinfo->sigd->rawCerts;
1676 }
1677
1678 /* find preferred encryption cert */
1679 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) &&
1680 (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1681 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
1682 { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! Find the cert. */
1683 ekp = SecCmsAttributeGetValue(attr);
1684 if (ekp == NULL)
1685 return NULL;
1686 cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, rawCerts, ekp);
1687 }
1688 if(cert) return cert;
1689
1690 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) &&
1691 (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1692 SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
1693 { /* we have a MS_SMIME_ENCRYPTION_KEY_PREFERENCE attribute! Find the cert. */
1694 ekp = SecCmsAttributeGetValue(attr);
1695 if (ekp == NULL)
1696 return NULL;
1697 cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, rawCerts, ekp);
1698 }
1699 return cert;
1700 }
1701
1702 /*
1703 * XXXX the following needs to be done in the S/MIME layer code
1704 * after signature of a signerinfo is verified
1705 */
1706 OSStatus
1707 SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo)
1708 {
1709 SecCertificateRef cert = NULL;
1710 CSSM_DATA_PTR profile = NULL;
1711 SecCmsAttribute *attr;
1712 CSSM_DATA_PTR utc_stime = NULL;
1713 CSSM_DATA_PTR ekp;
1714 int save_error;
1715 OSStatus rv;
1716 Boolean must_free_cert = PR_FALSE;
1717 OSStatus status;
1718 SecKeychainRef keychainOrArray;
1719
1720 status = SecKeychainCopyDefault(&keychainOrArray);
1721
1722 /* sanity check - see if verification status is ok (unverified does not count...) */
1723 if (signerinfo->verificationStatus != SecCmsVSGoodSignature)
1724 return SECFailure;
1725
1726 /* find preferred encryption cert */
1727 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) &&
1728 (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1729 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
1730 { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
1731 ekp = SecCmsAttributeGetValue(attr);
1732 if (ekp == NULL)
1733 return SECFailure;
1734
1735 /* we assume that all certs coming with the message have been imported to the */
1736 /* temporary database */
1737 cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, NULL, ekp);
1738 if (cert == NULL)
1739 return SECFailure;
1740 must_free_cert = PR_TRUE;
1741 }
1742
1743 if (cert == NULL) {
1744 /* no preferred cert found?
1745 * find the cert the signerinfo is signed with instead */
1746 CFStringRef emailAddress=NULL;
1747
1748 cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray);
1749 if (cert == NULL)
1750 return SECFailure;
1751 if (SecCertificateGetEmailAddress(cert,&emailAddress))
1752 return SECFailure;
1753 }
1754
1755 /* 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.
1756 * that's OK, we can still save the S/MIME profile. The encryption cert
1757 * should have already been saved */
1758 #ifdef notdef
1759 if (CERT_VerifyCert(keychainOrArray, cert, certUsageEmailRecipient, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1760 if (must_free_cert)
1761 CERT_DestroyCertificate(cert);
1762 return SECFailure;
1763 }
1764 #endif
1765
1766 /* XXX store encryption cert permanently? */
1767
1768 /*
1769 * Remember the current error set because we do not care about
1770 * anything set by the functions we are about to call.
1771 */
1772 save_error = PORT_GetError();
1773
1774 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
1775 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1776 SEC_OID_PKCS9_SMIME_CAPABILITIES,
1777 PR_TRUE);
1778 profile = SecCmsAttributeGetValue(attr);
1779 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1780 SEC_OID_PKCS9_SIGNING_TIME,
1781 PR_TRUE);
1782 utc_stime = SecCmsAttributeGetValue(attr);
1783 }
1784
1785 rv = CERT_SaveSMimeProfile (cert, profile, utc_stime);
1786 if (must_free_cert)
1787 CERT_DestroyCertificate(cert);
1788
1789 /*
1790 * Restore the saved error in case the calls above set a new
1791 * one that we do not actually care about.
1792 */
1793 PORT_SetError (save_error);
1794
1795 return rv;
1796 }
1797
1798 /*
1799 * SecCmsSignerInfoIncludeCerts - set cert chain inclusion mode for this signer
1800 */
1801 OSStatus
1802 SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo, SecCmsCertChainMode cm, SECCertUsage usage)
1803 {
1804 if (signerinfo->cert == NULL) {
1805 return SECFailure;
1806 }
1807
1808 /* don't leak if we get called twice */
1809 if (signerinfo->certList != NULL) {
1810 CFRelease(signerinfo->certList);
1811 signerinfo->certList = NULL;
1812 }
1813
1814 switch (cm) {
1815 case SecCmsCMNone:
1816 signerinfo->certList = NULL;
1817 break;
1818 case SecCmsCMCertOnly:
1819 signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
1820 break;
1821 case SecCmsCMCertChain:
1822 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE, PR_FALSE);
1823 break;
1824 case SecCmsCMCertChainWithRoot:
1825 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE, PR_FALSE);
1826 break;
1827 case SecCmsCMCertChainWithRootOrFail:
1828 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE, PR_TRUE);
1829 }
1830
1831 if (cm != SecCmsCMNone && signerinfo->certList == NULL) {
1832 return SECFailure;
1833 }
1834
1835 return SECSuccess;
1836 }