/* * SecCmsAttributeArrayAddAttr - add an attribute to an * array of attributes. */ OSStatus SecCmsAttributeArrayAddAttr(PLArenaPool *poolp, SecCmsAttribute ***attrs, SecCmsAttribute *attr) { SecCmsAttribute *oattr; void *mark; SECOidTag type; mark = PORT_ArenaMark(poolp); /* find oidtag of attr */ type = SecCmsAttributeGetType(attr); /* see if we have one already */ oattr = SecCmsAttributeArrayFindAttrByOidTag(*attrs, type, PR_FALSE); PORT_Assert (oattr == NULL); if (oattr != NULL) goto loser; /* XXX or would it be better to replace it? */ /* no, shove it in */ if (SecCmsArrayAdd(poolp, (void ***)attrs, (void *)attr) != SECSuccess) goto loser; PORT_ArenaUnmark(poolp, mark); return SECSuccess; loser: PORT_ArenaRelease(poolp, mark); return SECFailure; }
/* * SecCmsAttributeArraySetAttr - set an attribute's value in a set of attributes */ OSStatus SecCmsAttributeArraySetAttr(PLArenaPool *poolp, SecCmsAttribute ***attrs, SECOidTag type, CSSM_DATA_PTR value, Boolean encoded) { SecCmsAttribute *attr; void *mark; mark = PORT_ArenaMark(poolp); /* see if we have one already */ attr = SecCmsAttributeArrayFindAttrByOidTag(*attrs, type, PR_FALSE); if (attr == NULL) { /* not found? create one! */ attr = SecCmsAttributeCreate(poolp, type, value, encoded); if (attr == NULL) goto loser; /* and add it to the list */ if (SecCmsArrayAdd(poolp, (void ***)attrs, (void *)attr) != SECSuccess) goto loser; } else { /* found, shove it in */ /* XXX we need a decent memory model @#$#$!#!!! */ attr->values[0] = value; attr->encoded = encoded; } PORT_ArenaUnmark (poolp, mark); return SECSuccess; loser: PORT_ArenaRelease (poolp, mark); return SECFailure; }
/*! @function @abstract Return the data in the signed Codesigning Hash Agility attribute. @param sinfo SignerInfo data for this signer, pointer to a CFDataRef for attribute value @discussion Returns a CFDataRef containing the value of the attribute @result A return value of errSecInternal is an error trying to look up the oid. A status value of success with null result data indicates the attribute was not present. */ OSStatus SecCmsSignerInfoGetAppleCodesigningHashAgility(SecCmsSignerInfoRef sinfo, CFDataRef *sdata) { SecCmsAttribute *attr; CSSM_DATA_PTR value; if (sinfo == NULL || sdata == NULL) return paramErr; *sdata = NULL; if (sinfo->hashAgilityAttrValue != NULL) { *sdata = sinfo->hashAgilityAttrValue; /* cached copy */ return SECSuccess; } attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_HASH_AGILITY, PR_TRUE); /* attribute not found */ if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL) return SECSuccess; sinfo->hashAgilityAttrValue = CFDataCreate(NULL, value->Data, value->Length); /* make cached copy */ if (sinfo->hashAgilityAttrValue) { *sdata = sinfo->hashAgilityAttrValue; return SECSuccess; } return errSecAllocate; }
OSStatus SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy) { /* unAuthAttr is an array of attributes; we expect to see just one: the timestamp blob. If we have an unAuthAttr, but don't see a timestamp, return an error since we have no other cases where this would be present. */ SecCmsAttribute *attr = NULL; OSStatus status = SECFailure; require(signerinfo, xit); attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->unAuthAttr, SEC_OID_PKCS9_TIMESTAMP_TOKEN, PR_TRUE); if (attr == NULL) { status = errSecTimestampMissing; goto xit; } dprintf("found an id-ct-TSTInfo\n"); // Don't check the nonce in this case status = decodeTimeStampTokenWithPolicy(signerinfo, timeStampPolicy, (attr->values)[0], &signerinfo->encDigest, 0); xit: return status; }
/* * SecCmsSignerInfoGetSigningTime - return the signing time, * in UTCTime format, of a CMS signerInfo. * * sinfo - signerInfo data for this signer * * Returns a pointer to XXXX (what?) * A return value of NULL is an error. */ OSStatus SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime) { SecCmsAttribute *attr; CSSM_DATA_PTR value; if (sinfo == NULL) return paramErr; if (sinfo->signingTime != 0) { *stime = sinfo->signingTime; /* cached copy */ return SECSuccess; } attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); /* XXXX multi-valued attributes NIH */ if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL) return errSecSigningTimeMissing; if (DER_UTCTimeToCFDate(value, stime) != SECSuccess) return errSecSigningTimeMissing; sinfo->signingTime = *stime; /* make cached copy */ return SECSuccess; }
OSStatus SecCmsSignerInfoVerifyWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType) { SecPublicKeyRef publickey = NULL; SecCmsAttribute *attr; CSSM_DATA encoded_attrs; SecCertificateRef cert; SecCmsVerificationStatus vs = SecCmsVSUnverified; PLArenaPool *poolp; SECOidTag digestAlgTag, digestEncAlgTag; if (signerinfo == NULL) return SECFailure; /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */ /* cert has not been verified */ if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL)) == NULL) { dprintf("SecCmsSignerInfoVerify: no signing cert\n"); vs = SecCmsVSSigningCertNotFound; goto loser; } dprintfRC("SecCmsSignerInfoVerify top: cert %p cert.rc %d\n", cert, (int)CFGetRetainCount(cert)); debugShowSigningCertificate(signerinfo); OSStatus status; if ((status = SecCertificateCopyPublicKey(cert, &publickey))) { syslog(LOG_ERR, "SecCmsSignerInfoVerifyWithPolicy: copy public key failed %d", (int)status); vs = SecCmsVSProcessingError; goto loser; } digestAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg)); digestEncAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)); /* * Gross hack necessitated by RFC 3278 section 2.1.1, which states * that the signature algorithm (here, digestEncAlg) contains ecdsa_with-SHA1, * *not* (as in all other algorithms) the raw signature algorithm, e.g. * pkcs1RSAEncryption. */ if(digestEncAlgTag == SEC_OID_ECDSA_WithSHA1) { digestEncAlgTag = SEC_OID_EC_PUBLIC_KEY; } if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) { if (contentType) { /* * Check content type * * RFC2630 sez that if there are any authenticated attributes, * then there must be one for content type which matches the * content type of the content being signed, and there must * be one for message digest which matches our message digest. * So check these things first. */ if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE)) == NULL) { vs = SecCmsVSMalformedSignature; goto loser; } if (SecCmsAttributeCompareValue(attr, contentType) == PR_FALSE) { vs = SecCmsVSMalformedSignature; goto loser; } } /* * Check digest */ if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE)) == NULL) { vs = SecCmsVSMalformedSignature; goto loser; } if (SecCmsAttributeCompareValue(attr, digest) == PR_FALSE) { vs = SecCmsVSDigestMismatch; goto loser; } if ((poolp = PORT_NewArena (1024)) == NULL) { vs = SecCmsVSProcessingError; goto loser; } /* * Check signature * * The signature is based on a digest of the DER-encoded authenticated * attributes. So, first we encode and then we digest/verify. * we trust the decoder to have the attributes in the right (sorted) order */ encoded_attrs.Data = NULL; encoded_attrs.Length = 0; if (SecCmsAttributeArrayEncode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL || encoded_attrs.Data == NULL || encoded_attrs.Length == 0) { vs = SecCmsVSProcessingError; goto loser; } vs = (VFY_VerifyData (encoded_attrs.Data, (int)encoded_attrs.Length, publickey, &(signerinfo->encDigest), digestAlgTag, digestEncAlgTag, signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature; dprintf("VFY_VerifyData (authenticated attributes): %s\n", (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature"); PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */ } else { CSSM_DATA_PTR sig; /* No authenticated attributes. The signature is based on the plain message digest. */ sig = &(signerinfo->encDigest); if (sig->Length == 0) goto loser; vs = (VFY_VerifyDigest(digest, publickey, sig, digestAlgTag, digestEncAlgTag, signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature; dprintf("VFY_VerifyData (plain message digest): %s\n", (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature"); } if (!SecCmsArrayIsEmpty((void **)signerinfo->unAuthAttr)) { dprintf("found an unAuthAttr\n"); OSStatus rux = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo,timeStampPolicy); dprintf("SecCmsSignerInfoVerifyUnAuthAttrs Status: %ld\n", (long)rux); if (rux) { goto loser; } } if (vs == SecCmsVSBadSignature) { /* * XXX Change the generic error into our specific one, because * in that case we get a better explanation out of the Security * Advisor. This is really a bug in our error strings (the * "generic" error has a lousy/wrong message associated with it * which assumes the signature verification was done for the * purposes of checking the issuer signature on a certificate) * but this is at least an easy workaround and/or in the * Security Advisor, which specifically checks for the error * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation * in that case but does not similarly check for * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would * probably say the wrong thing in the case that it *was* the * certificate signature check that failed during the cert * verification done above. Our error handling is really a mess. */ if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE) PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE); } if (publickey != NULL) CFRelease(publickey); signerinfo->verificationStatus = vs; dprintfRC("SecCmsSignerInfoVerify end: cerp %p cert.rc %d\n", cert, (int)CFGetRetainCount(cert)); dprintf("verificationStatus: %d\n", vs); return (vs == SecCmsVSGoodSignature) ? SECSuccess : SECFailure; loser: if (publickey != NULL) SECKEY_DestroyPublicKey (publickey); dprintf("verificationStatus2: %d\n", vs); signerinfo->verificationStatus = vs; PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); return SECFailure; }
/* * XXXX the following needs to be done in the S/MIME layer code * after signature of a signerinfo is verified */ OSStatus SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo) { SecCertificateRef cert = NULL; CSSM_DATA_PTR profile = NULL; SecCmsAttribute *attr; CSSM_DATA_PTR utc_stime = NULL; CSSM_DATA_PTR ekp; int save_error; OSStatus rv; Boolean must_free_cert = PR_FALSE; OSStatus status; SecKeychainRef keychainOrArray; status = SecKeychainCopyDefault(&keychainOrArray); /* sanity check - see if verification status is ok (unverified does not count...) */ if (signerinfo->verificationStatus != SecCmsVSGoodSignature) return SECFailure; /* find preferred encryption cert */ if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) && (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL) { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */ ekp = SecCmsAttributeGetValue(attr); if (ekp == NULL) return SECFailure; /* we assume that all certs coming with the message have been imported to the */ /* temporary database */ cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, ekp); if (cert == NULL) return SECFailure; must_free_cert = PR_TRUE; } if (cert == NULL) { /* no preferred cert found? * find the cert the signerinfo is signed with instead */ CFStringRef emailAddress=NULL; cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray); if (cert == NULL) return SECFailure; if (SecCertificateGetEmailAddress(cert,&emailAddress)) return SECFailure; } /* 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. * that's OK, we can still save the S/MIME profile. The encryption cert * should have already been saved */ #ifdef notdef if (CERT_VerifyCert(keychainOrArray, cert, certUsageEmailRecipient, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) { if (must_free_cert) CERT_DestroyCertificate(cert); return SECFailure; } #endif /* XXX store encryption cert permanently? */ /* * Remember the current error set because we do not care about * anything set by the functions we are about to call. */ save_error = PORT_GetError(); if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) { attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_SMIME_CAPABILITIES, PR_TRUE); profile = SecCmsAttributeGetValue(attr); attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); utc_stime = SecCmsAttributeGetValue(attr); } rv = CERT_SaveSMimeProfile (cert, profile, utc_stime); if (must_free_cert) CERT_DestroyCertificate(cert); /* * Restore the saved error in case the calls above set a new * one that we do not actually care about. */ PORT_SetError (save_error); return rv; }