]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_smime/lib/cmssiginfo.c
Security-58286.41.2.tar.gz
[apple/security.git] / libsecurity_smime / lib / cmssiginfo.c
index 41ab2e23638b9cd82dfdab5e465def045c26c941..21d9fd9a2dcbb0d3aae86a993476ff99d4d2c5ff 100644 (file)
@@ -59,6 +59,8 @@
 #include <Security/SecKeyPriv.h>
 #include <utilities/SecCFWrappers.h>
 #include <CoreFoundation/CFTimeZone.h>
+#include <Security/SecBasePriv.h>
+#include <Security/SecItem.h>
 
 
 #define HIDIGIT(v) (((v) / 10) + '0')    
@@ -141,18 +143,15 @@ DER_CFDateToUTCTime(CFAbsoluteTime date, SecAsn1Item * utcTime)
     utcTime->Data = d = PORT_Alloc(13);
     if (!utcTime->Data)
        return SECFailure;
-
-    int year;
-    int month;
-    int day;
-    int hour;
-    int minute;
-    int second;
     
-    if (!CFCalendarDecomposeAbsoluteTime(SecCFCalendarGetZulu(), date, "yMdHms", &year, &month, &day, &hour, &minute, &second))
+    __block int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
+    __block bool result;
+    SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar) {
+        result = CFCalendarDecomposeAbsoluteTime(zuluCalendar, date, "yMdHms", &year, &month, &day, &hour, &minute, &second);
+    });
+    if (!result)
         return SECFailure;
 
-
     /* UTC time does not handle the years before 1950 */
     if (year < 1950)
         return SECFailure;
@@ -195,12 +194,22 @@ SecCmsSignerInfoCreate(SecCmsSignedDataRef sigd, SecIdentityRef identity, SECOid
     SecCmsSignerInfoRef signerInfo = NULL;
     SecCertificateRef cert = NULL;
     SecPrivateKeyRef signingKey = NULL;
+    CFDictionaryRef keyAttrs = NULL;
 
     if (SecIdentityCopyCertificate(identity, &cert))
        goto loser;
     if (SecIdentityCopyPrivateKey(identity, &signingKey))
        goto loser;
 
+    /* In some situations, the "Private Key" in the identity is actually a public key. Check. */
+    keyAttrs = SecKeyCopyAttributes(signingKey);
+    if (!keyAttrs)
+        goto loser;
+    CFTypeRef class = CFDictionaryGetValue(keyAttrs, kSecAttrKeyClass);
+    if (!class || (CFGetTypeID(class) != CFStringGetTypeID()) || !CFEqual(class, kSecAttrKeyClassPrivate)) {
+        goto loser;
+    }
+
     signerInfo = nss_cmssignerinfo_create(sigd, SecCmsSignerIDIssuerSN, cert, NULL, NULL, signingKey, digestalgtag);
 
 loser:
@@ -208,6 +217,8 @@ loser:
        CFRelease(cert);
     if (signingKey)
        CFRelease(signingKey);
+    if (keyAttrs)
+        CFRelease(keyAttrs);
 
     return signerInfo;
 }
@@ -247,8 +258,10 @@ nss_cmssignerinfo_create(SecCmsSignedDataRef sigd, SecCmsSignerIDSelector type,
         if (!subjKeyID)
             goto loser;
         signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, SecAsn1Item);
-        SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
-                         subjKeyID);
+        if (SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
+                             subjKeyID)) {
+            goto loser;
+        }
         signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
         if (!signerinfo->pubKey)
             goto loser;
@@ -291,11 +304,17 @@ loser:
 void
 SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si)
 {
-    if (si->cert != NULL)
-       CERT_DestroyCertificate(si->cert);
+    if (si->cert != NULL) {
+        CERT_DestroyCertificate(si->cert);
+    }
 
-    if (si->certList != NULL) 
-       CFRelease(si->certList);
+    if (si->certList != NULL) {
+        CFRelease(si->certList);
+    }
+
+    if (si->hashAgilityAttrValue != NULL) {
+        CFRelease(si->hashAgilityAttrValue);
+    }
 
     /* XXX storage ??? */
 }
@@ -323,7 +342,7 @@ SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, SecAs
     SECOidTag pubkAlgTag;
     SecAsn1Item signature = { 0 };
     OSStatus rv;
-    PLArenaPool *poolp, *tmppoolp;
+    PLArenaPool *poolp, *tmppoolp = NULL;
     const SECAlgorithmID *algID = NULL;
     //CERTSubjectPublicKeyInfo *spki;
 
@@ -331,6 +350,8 @@ SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, SecAs
 
     poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
 
+    SecAsn1AlgId _algID;
+
     switch (signerinfo->signerIdentifier.identifierType) {
     case SecCmsSignerIDIssuerSN:
         privkey = signerinfo->signingKey;
@@ -342,7 +363,7 @@ SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, SecAs
            goto loser;
         }
 #else
-        SecAsn1AlgId _algID = SecCertificateGetPublicKeyAlgorithmID(cert);
+        _algID = SecCertificateGetPublicKeyAlgorithmID(cert);
         algID = &_algID;
 #endif
         break;
@@ -373,6 +394,13 @@ SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, SecAs
     }
     digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
     pubkAlgTag = SECOID_GetAlgorithmTag(algID);
+
+    /* we no longer support signing with MD5 */
+    if (digestalgtag == SEC_OID_MD5) {
+        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+        goto loser;
+    }
+
 #if USE_CDSA_CRYPTO
     if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) {
       SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
@@ -447,6 +475,7 @@ SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, SecAs
 #endif
 
        PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
+       tmppoolp = 0;
     } else {
         signature.Length = SecKeyGetSize(privkey, kSecKeySignatureSize);
         signature.Data = PORT_ZAlloc(signature.Length);
@@ -484,6 +513,8 @@ loser:
        SECITEM_FreeItem (&signature, PR_FALSE);
     if (privkey)
        SECKEY_DestroyPrivateKey(privkey);
+    if (tmppoolp)
+       PORT_FreeArena(tmppoolp, PR_FALSE);
     return SECFailure;
 }
 
@@ -532,6 +563,17 @@ SecCmsSignerInfoCopySigningCertificates(SecCmsSignerInfoRef signerinfo)
             CFRelease(cert);
         }
     }
+
+    if ((CFArrayGetCount(certs) == 0) &&
+        (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID))
+    {
+        SecCertificateRef cert = CERT_FindCertificateBySubjectKeyID(signerinfo->signedData->certs,
+                                                                    signerinfo->signerIdentifier.id.subjectKeyID);
+        if (cert) {
+            CFArrayAppendValue(certs, cert);
+            CFRelease(cert);
+        }
+    }
     return certs;
 }
 #endif
@@ -573,9 +615,17 @@ SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef
     {
        if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT)
        {
-           /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */
-           if (signerinfo->verificationStatus == SecCmsVSGoodSignature)
-               signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted;
+               /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */
+#if 0
+#warning DEBUG - SecCmsSignerInfoVerifyCertificate trusts everything!
+               if (signerinfo->verificationStatus == SecCmsVSGoodSignature) {
+                        syslog(LOG_ERR, "SecCmsSignerInfoVerifyCertificate ignoring SEC_ERROR_UNTRUSTED_CERT");
+                        rv = SECSuccess;
+               }
+#else
+               if (signerinfo->verificationStatus == SecCmsVSGoodSignature)
+                       signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted;
+#endif
        }
     }
 
@@ -597,7 +647,6 @@ SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, Sec
     SecCertificateRef cert;
     SecCmsVerificationStatus vs = SecCmsVSUnverified;
     PLArenaPool *poolp;
-    SECOidTag digestAlgTag, digestEncAlgTag;
 
     if (signerinfo == NULL)
        return SECFailure;
@@ -620,8 +669,6 @@ SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, Sec
         goto loser;
 #endif
 
-    digestAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg));
-    digestEncAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
     if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
        if (contentType) {
            /*
@@ -814,6 +861,44 @@ SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime)
     return SECSuccess;
 }
 
+/*!
+     @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;
+    SecAsn1Item *value;
+
+    if (sinfo == NULL || sdata == NULL)
+        return errSecParam;
+
+    *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;
+}
+
 /*
  * Return the signing cert of a CMS signerInfo.
  *
@@ -886,6 +971,10 @@ SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo, SecKeychai
         cert = CERT_FindCertificateByIssuerAndSN(signerinfo->signedData->certs, signerinfo->signerIdentifier.id.issuerAndSN);
         signerinfo->cert = cert;
     }
+    if (!signerinfo->cert && (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID)) {
+        cert = CERT_FindCertificateBySubjectKeyID(signerinfo->signedData->certs, signerinfo->signerIdentifier.id.subjectKeyID);
+        signerinfo->cert = cert;
+    }
 #endif
 
     return cert;
@@ -1197,6 +1286,81 @@ SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo,
     return SECFailure;
 }
 
+/*!
+     @function
+     @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
+     @discussion This is expected to be included in outgoing signed Apple code signatures.
+ */
+OSStatus
+SecCmsSignerInfoAddAppleCodesigningHashAgility(SecCmsSignerInfoRef signerinfo, CFDataRef attrValue)
+{
+    SecCmsAttribute *attr;
+    PLArenaPool *poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
+    void *mark = PORT_ArenaMark(poolp);
+    OSStatus status = SECFailure;
+
+    /* The value is required for this attribute. */
+    if (!attrValue) {
+        status = errSecParam;
+        goto loser;
+    }
+
+    /*
+     * SecCmsAttributeCreate makes a copy of the data in value, so
+     * we don't need to copy into the CSSM_DATA struct.
+     */
+    SecAsn1Item value;
+    value.Length = CFDataGetLength(attrValue);
+    value.Data = (uint8_t *)CFDataGetBytePtr(attrValue);
+
+    if ((attr = SecCmsAttributeCreate(poolp,
+                                      SEC_OID_APPLE_HASH_AGILITY,
+                                      &value,
+                                      PR_FALSE)) == NULL) {
+        status = errSecAllocate;
+        goto loser;
+    }
+
+    if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
+        status = errSecInternal;
+        goto loser;
+    }
+
+    PORT_ArenaUnmark(poolp, mark);
+    return SECSuccess;
+
+loser:
+    PORT_ArenaRelease(poolp, mark);
+    return status;
+}
+
+SecCertificateRef SecCmsSignerInfoCopyCertFromEncryptionKeyPreference(SecCmsSignerInfoRef signerinfo) {
+    SecCertificateRef cert = NULL;
+    SecCmsAttribute *attr;
+    SecAsn1Item *ekp;
+
+    /* sanity check - see if verification status is ok (unverified does not count...) */
+    if (signerinfo->verificationStatus != SecCmsVSGoodSignature)
+        return NULL;
+
+    /* 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! Find the cert. */
+        ekp = SecCmsAttributeGetValue(attr);
+        if (ekp == NULL)
+            return NULL;
+
+        SecAsn1Item **rawCerts = NULL;
+        if (signerinfo->signedData) {
+            rawCerts = signerinfo->signedData->rawCerts;
+        }
+        cert = SecSMIMEGetCertFromEncryptionKeyPreference(rawCerts, ekp);
+    }
+    return cert;
+}
+
 /*
  * XXXX the following needs to be done in the S/MIME layer code
  * after signature of a signerinfo is verified