VirtualBox

Changeset 74733 in vbox for trunk/src/VBox/Runtime


Ignore:
Timestamp:
Oct 10, 2018 11:22:07 AM (6 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
125663
Message:

IPRT/ldr: More Mach-O code signing validation hacking. bugref:9232

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/ldr/ldrMachO.cpp

    r74727 r74733  
    38933893        void *pv = RTMemAllocZ(RT_ALIGN_Z(pThis->cbCodeSignature, 16));
    38943894        AssertReturn(pv, VERR_NO_MEMORY);
    3895         int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pv, pThis->cbCodeSignature, pThis->offCodeSignature);
     3895        int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pv, pThis->cbCodeSignature,
     3896                                              pThis->offImage + pThis->offCodeSignature);
    38963897        if (RT_SUCCESS(rc))
    38973898        {
     
    42934294
    42944295            /** @todo idPlatform values.   */
    4295 
     4296            /** @todo Check for gaps if we know the version number?  Alignment?  */
     4297
     4298            /* If first code directory, check that the code limit covers the whole image up to the signature data. */
     4299            if (pSignature->cCodeDirs == 0)
     4300            {
     4301                /** @todo verify the that the signature data is at the very end... */
     4302                if (cbCodeLimit32 != pThis->offCodeSignature)
     4303                    return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4304                                         "Slot #%u: Unexpected code limit: %#x, expected %#x",
     4305                                         iSlot, cbCodeLimit32, pThis->offCodeSignature);
     4306            }
     4307            /* Otherwise, check that the code limit matches the previous directories. */
     4308            else
     4309                for (uint32_t i = 0; i < pSignature->cCodeDirs; i++)
     4310                    if (pSignature->aCodeDirs[i].pCodeDir->cbCodeLimit32 != pCodeDir->cbCodeLimit32)
     4311                        return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4312                                             "Slot #%u: Code limit differs from previous directory: %#x, expected %#x",
     4313                                             iSlot, cbCodeLimit32, RT_BE2H_U32(pSignature->aCodeDirs[i].pCodeDir->cbCodeLimit32));
    42964314
    42974315            /* Commit the code dir entry: */
     
    43624380
    43634381/**
     4382 * Worker for rtldrMachO_VerifySignatureValidatePkcs7Hashes that handles plists
     4383 * with code directory hashes inside them.
     4384 *
     4385 * It is assumed that these plist files was invented to handle alternative code
     4386 * directories.
     4387 *
     4388 * @note    Putting an XML plist into the authenticated attribute list was
     4389 *          probably not such a great idea, given all the optional and
     4390 *          adjustable white-space padding.  We should probably validate
     4391 *          everything very strictly, limiting the elements, require certain
     4392 *          attribute lists and even have strict expectations about the
     4393 *          white-space, but right now let just make sure it's xml and get the
     4394 *          data in the cdhashes array.
     4395 */
     4396static int rtldrMachO_VerifySignatureValidateCdHashesPlist(PRTLDRMACHOSIGNATURE pSignature, char *pszPlist,
     4397                                                           uint8_t *pbHash, uint32_t cbHash, PRTERRINFO pErrInfo)
     4398{
     4399    const char * const pszStart = pszPlist;
     4400#define CHECK_AND_SKIP_OR_RETURN(a_szLead) \
     4401    do { \
     4402        if (!RTStrNICmp(pszPlist, RT_STR_TUPLE(a_szLead))) \
     4403            pszPlist += sizeof(a_szLead) - 1; \
     4404        else return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, \
     4405                                  "Expected '%s' found '%.16s...' at %#zu in plist", a_szLead, pszPlist, pszPlist - pszStart); \
     4406    } while (0)
     4407#define CHECK_CASE_AND_SKIP_OR_RETURN(a_szLead) \
     4408    do { \
     4409        if (!RTStrNCmp(pszPlist, RT_STR_TUPLE(a_szLead))) \
     4410            pszPlist += sizeof(a_szLead) - 1; \
     4411        else return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, \
     4412                                  "Expected '%s' found '%.16s...' at %#zu in plist", a_szLead, pszPlist, pszPlist - pszStart); \
     4413    } while (0)
     4414
     4415    /* <?xml version="1.0" encoding="UTF-8"?> */
     4416    CHECK_CASE_AND_SKIP_OR_RETURN("<?xml");
     4417    RT_NOREF(pSignature, pbHash, cbHash);
     4418    return VERR_NOT_IMPLEMENTED;
     4419}
     4420
     4421
     4422/**
    43644423 * Verifies the code directory hashes embedded in the PKCS\#7 data.
    43654424 *
     
    43704429static int rtldrMachO_VerifySignatureValidatePkcs7Hashes(PRTLDRMACHOSIGNATURE pSignature, PRTERRINFO pErrInfo)
    43714430{
    4372     RT_NOREF(pSignature, pErrInfo);
    4373     return VERR_NOT_IMPLEMENTED;
     4431    /*
     4432     * Look thru the authenticated attributes in the signer info array.
     4433     */
     4434    PRTCRPKCS7SIGNEDDATA pSignedData = pSignature->pSignedData;
     4435    for (uint32_t iSignerInfo = 0; iSignerInfo < pSignedData->SignerInfos.cItems; iSignerInfo++)
     4436    {
     4437        PCRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[iSignerInfo];
     4438        bool                  fMsgDigest  = false;
     4439        bool                  fPlist      = false;
     4440        for (uint32_t iAttrib = 0; iAttrib < pSignerInfo->AuthenticatedAttributes.cItems; iAttrib++)
     4441        {
     4442            PCRTCRPKCS7ATTRIBUTE pAttrib = pSignerInfo->AuthenticatedAttributes.papItems[iAttrib];
     4443            if (RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_MESSAGE_DIGEST_OID) == 0)
     4444            {
     4445                /*
     4446                 * Validate the message digest while we're here.
     4447                 */
     4448                AssertReturn(pAttrib->uValues.pOctetStrings && pAttrib->uValues.pOctetStrings->cItems == 1, VERR_INTERNAL_ERROR_5);
     4449
     4450                RTCRDIGEST hDigest;
     4451                int rc = RTCrDigestCreateByObjId(&hDigest, &pSignerInfo->DigestAlgorithm.Algorithm);
     4452                if (RT_SUCCESS(rc))
     4453                {
     4454                    rc = RTCrDigestUpdate(hDigest, pSignature->aCodeDirs[0].pCodeDir, pSignature->aCodeDirs[0].cb);
     4455                    if (RT_SUCCESS(rc))
     4456                    {
     4457                        if (!RTCrDigestMatch(hDigest,
     4458                                             pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pv,
     4459                                             pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb))
     4460                            return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MESSAGE_DIGEST_ATTRIB_MISMATCH,
     4461                                                 "Authenticated message-digest attribute mismatch:\n"
     4462                                                 "signed: %.*Rhxs\n"
     4463                                                 "our:    %.*Rhxs\n",
     4464                                                 pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb,
     4465                                                 pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pv,
     4466                                                 RTCrDigestGetHashSize(hDigest), RTCrDigestGetHash(hDigest));
     4467                    }
     4468                    else
     4469                        rc = RTErrInfoSetF(pErrInfo, rc, "RTCrDigestUpdate failed: %Rrc", rc);
     4470                }
     4471                else
     4472                    rc = RTErrInfoSetF(pErrInfo, rc, "Failed to create a digest for OID %s: %Rrc",
     4473                                       pSignerInfo->DigestAlgorithm.Algorithm.szObjId, rc);
     4474                if (RT_FAILURE(rc))
     4475                    return rc;
     4476                fMsgDigest = true;
     4477            }
     4478            else if (pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_APPLE_MULTI_CD_PLIST)
     4479            {
     4480                /*
     4481                 * An XML (better be) property list with code directory hashes in it.
     4482                 */
     4483                if (!pAttrib->uValues.pOctetStrings || pAttrib->uValues.pOctetStrings->cItems != 1)
     4484                    return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Bad authenticated plist attribute");
     4485
     4486                uint32_t    cch = pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb;
     4487                char const *pch = pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pch;
     4488                int rc = RTStrValidateEncodingEx(pch, cch, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
     4489                if (RT_FAILURE(rc))
     4490                    return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4491                                         "Authenticated plist attribute is not valid UTF-8: %Rrc", rc);
     4492                uint32_t const cchMin = sizeof("<?xml?><plist><dict><key>cdhashes</key><array><data>hul2SSkDQFRXbGlt3AmCp25MU0Y=</data></array></dict></plist>") - 1;
     4493                if (cch < cchMin)
     4494                    return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4495                                         "Authenticated plist attribute is too short: %#x, min: %#x", cch, cchMin);
     4496                if (cch > _64K)
     4497                    return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4498                                         "Authenticated plist attribute is too long: %#x, max: 64KB", cch, cchMin);
     4499
     4500                /* Copy the plist into a buffer and zero terminate it.  Also allocate room for decoding a hash. */
     4501                const uint32_t cbMaxHash = 128;
     4502                char *pszTmp = (char *)RTMemTmpAlloc(cbMaxHash + cch + 3);
     4503                if (!pszTmp)
     4504                    return VERR_NO_TMP_MEMORY;
     4505                pszTmp[cbMaxHash + cch] = '\0';
     4506                pszTmp[cbMaxHash + cch + 1] = '\0';
     4507                pszTmp[cbMaxHash + cch + 2] = '\0';
     4508                rc = rtldrMachO_VerifySignatureValidateCdHashesPlist(pSignature, (char *)memcpy(pszTmp + cbMaxHash, pch, cch),
     4509                                                                     (uint8_t *)pszTmp, cbMaxHash, pErrInfo);
     4510                RTMemTmpFree(pszTmp);
     4511                if (RT_FAILURE(rc))
     4512                    return rc;
     4513                fPlist = true;
     4514            }
     4515        }
     4516        if (!fMsgDigest && pSignature->cCodeDirs > 1)
     4517            return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Missing authenticated message-digest attribute");
     4518        if (!fPlist && pSignature->cCodeDirs > 1)
     4519            return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Missing authenticated code directory hash plist attribute");
     4520    }
     4521    if (pSignedData->SignerInfos.cItems < 1)
     4522        return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "PKCS#7 signed data contains no signatures");
     4523
     4524    return VINF_SUCCESS;
    43744525}
    43754526
     
    43814532 * @param   pThis           The Mach-O module instance.
    43824533 * @param   pEntry          The data entry for the code directory to validate.
     4534 * @param   pbBuf           Read buffer.
     4535 * @param   cbBuf           Buffer size.
    43834536 * @param   pErrInfo        Where to supply extra error details. Optional.
    43844537 */
    4385 static int rtldrMachO_VerifySignatureValidateCodeDir(PRTLDRMODMACHO pThis, PRTLDRMACHCODEDIR pEntry, PRTERRINFO pErrInfo)
    4386 {
    4387     RT_NOREF(pThis, pEntry, pErrInfo);
    4388     return VERR_NOT_IMPLEMENTED;
     4538static int rtldrMachO_VerifySignatureValidateCodeDir(PRTLDRMODMACHO pThis, PRTLDRMACHCODEDIR pEntry,
     4539                                                     uint8_t *pbBuf, uint32_t cbBuf, PRTERRINFO pErrInfo)
     4540{
     4541    RTCRDIGEST hDigest;
     4542    int rc = RTCrDigestCreateByType(&hDigest, pEntry->enmDigest);
     4543    if (RT_SUCCESS(rc))
     4544    {
     4545        PCRTCRAPLCSCODEDIRECTORY pCodeDir    = pEntry->pCodeDir;
     4546        PRTLDRREADER const       pRdr        = pThis->Core.pReader;
     4547        uint32_t                 cbCodeLimit = RT_BE2H_U32(pCodeDir->cbCodeLimit32);
     4548        uint32_t const           cbPage      = RT_BIT_32(pCodeDir->cPageShift);
     4549        uint32_t const           cHashes     = RT_BE2H_U32(pCodeDir->cCodeSlots);
     4550        uint8_t const            cbHash      = pCodeDir->cbHash;
     4551        uint8_t const           *pbHash      = (uint8_t const *)pCodeDir + RT_BE2H_U32(pCodeDir->offHashSlots);
     4552        RTFOFF                   offFile     = pThis->offImage;
     4553        if (   RT_BE2H_U32(pCodeDir->uVersion) < RTCRAPLCS_VER_SUPPORTS_SCATTER
     4554            || pCodeDir->offScatter == 0)
     4555        {
     4556            /*
     4557             * Work the image in linear fashion.
     4558             */
     4559            for (uint32_t iHash = 0; iHash < cHashes; iHash++, pbHash += cbHash, cbCodeLimit -= cbPage)
     4560            {
     4561                RTFOFF const offPage = offFile;
     4562
     4563                /*
     4564                 * Read and digest the data for the current hash page.
     4565                 */
     4566                rc = RTCrDigestReset(hDigest);
     4567                AssertRCBreak(rc);
     4568                Assert(cbCodeLimit > cbPage || iHash + 1 == cHashes);
     4569                uint32_t cbLeft = iHash + 1 < cHashes ? cbPage : cbCodeLimit;
     4570                while (cbLeft > 0)
     4571                {
     4572                    uint32_t const cbToRead = RT_MIN(cbBuf, cbLeft);
     4573                    rc = pRdr->pfnRead(pRdr, pbBuf, cbToRead, offFile);
     4574                    AssertRCBreak(rc);
     4575
     4576                    rc = RTCrDigestUpdate(hDigest, pbBuf, cbToRead);
     4577                    AssertRCBreak(rc);
     4578
     4579                    offFile += cbToRead;
     4580                    cbLeft  -= cbToRead;
     4581                }
     4582                AssertRCBreak(rc);
     4583                rc = RTCrDigestFinal(hDigest, NULL, 0);
     4584                AssertRCBreak(rc);
     4585
     4586                /*
     4587                 * Compare it.
     4588                 * Note! Don't use RTCrDigestMatch here as there is a truncated SHA-256 variant.
     4589                 */
     4590                if (memcmp(pbHash, RTCrDigestGetHash(hDigest), cbHash) != 0)
     4591                {
     4592                    rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_MISMATCH,
     4593                                       "Hash #%u (@%RX64 LB %#x) mismatch in code dir #%u: %.*Rhxs, expected %.*Rhxs",
     4594                                       iHash, offPage, cbPage, pEntry->uSlot, (int)cbHash, pbHash,
     4595                                       (int)cbHash, RTCrDigestGetHash(hDigest));
     4596                    break;
     4597                }
     4598
     4599            }
     4600        }
     4601        /*
     4602         * Work the image in scattered fashion.
     4603         */
     4604        else
     4605            rc = VERR_INTERNAL_ERROR_4;
     4606
     4607        RTCrDigestRelease(hDigest);
     4608    }
     4609    return rc;
    43894610}
    43904611
     
    44004621static int rtldrMachO_VerifySignatureValidateCodeDirs(PRTLDRMODMACHO pThis, PRTLDRMACHOSIGNATURE pSignature, PRTERRINFO pErrInfo)
    44014622{
    4402     int rc = VERR_INTERNAL_ERROR_3;
    4403     for (uint32_t i = 0; i < pSignature->cCodeDirs; i++)
    4404     {
    4405         rc = rtldrMachO_VerifySignatureValidateCodeDir(pThis, &pSignature->aCodeDirs[i], pErrInfo);
    4406         if (RT_FAILURE(rc))
    4407             break;
    4408     }
    4409     return rc;
     4623    void *pvBuf = RTMemTmpAllocZ(_4K);
     4624    if (pvBuf)
     4625    {
     4626        int rc = VERR_INTERNAL_ERROR_3;
     4627        for (uint32_t i = 0; i < pSignature->cCodeDirs; i++)
     4628        {
     4629            rc = rtldrMachO_VerifySignatureValidateCodeDir(pThis, &pSignature->aCodeDirs[i], (uint8_t *)pvBuf, _4K, pErrInfo);
     4630            if (RT_FAILURE(rc))
     4631                break;
     4632        }
     4633        RTMemTmpFree(pvBuf);
     4634        return rc;
     4635    }
     4636    return VERR_NO_TMP_MEMORY;
    44104637}
    44114638
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette