示例#1
0
/**
 * Verifies the manifest and its signature.
 *
 * @returns VBox status code, failures with message.
 * @param   hOurManifest    The manifest we compiled.
 * @param   hManifestFile   The manifest file in the extension pack.
 * @param   hSignatureFile  The manifest signature file.
 * @param   pszError            Where to store an error message on failure.
 * @param   cbError             The size of the buffer @a pszError points to.
 */
static int vboxExtPackVerifyManifestAndSignature(RTMANIFEST hOurManifest, RTVFSFILE hManifestFile, RTVFSFILE hSignatureFile,
                                                 char *pszError, size_t cbError)
{
    /*
     * Read the manifest from the extension pack.
     */
    int rc = RTVfsFileSeek(hManifestFile, 0, RTFILE_SEEK_BEGIN, NULL);
    if (RT_FAILURE(rc))
        return vboxExtPackReturnError(rc, pszError, cbError, "RTVfsFileSeek failed: %Rrc", rc);

    RTMANIFEST hTheirManifest;
    rc = RTManifestCreate(0 /*fFlags*/, &hTheirManifest);
    if (RT_FAILURE(rc))
        return vboxExtPackReturnError(rc, pszError, cbError, "RTManifestCreate failed: %Rrc", rc);

    RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hManifestFile);
    rc = RTManifestReadStandard(hTheirManifest, hVfsIos);
    RTVfsIoStrmRelease(hVfsIos);
    if (RT_SUCCESS(rc))
    {
        /*
         * Compare the manifests.
         */
        static const char *s_apszIgnoreEntries[] =
        {
            VBOX_EXTPACK_MANIFEST_NAME,
            VBOX_EXTPACK_SIGNATURE_NAME,
            "./" VBOX_EXTPACK_MANIFEST_NAME,
            "./" VBOX_EXTPACK_SIGNATURE_NAME,
            NULL
        };
        char szError[RTPATH_MAX];
        rc = RTManifestEqualsEx(hOurManifest, hTheirManifest, &s_apszIgnoreEntries[0], NULL,
                                RTMANIFEST_EQUALS_IGN_MISSING_ATTRS /*fFlags*/,
                                szError, sizeof(szError));
        if (RT_SUCCESS(rc))
        {
            /*
             * Validate the manifest file signature.
             */
            /** @todo implement signature stuff */
            NOREF(hSignatureFile);

        }
        else if (rc == VERR_NOT_EQUAL && szError[0])
            vboxExtPackSetError(pszError, cbError, "Manifest mismatch: %s", szError);
        else
            vboxExtPackSetError(pszError, cbError, "RTManifestEqualsEx failed: %Rrc", rc);
#if 0
        RTVFSIOSTREAM hVfsIosStdOut = NIL_RTVFSIOSTREAM;
        RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE, true, &hVfsIosStdOut);
        RTVfsIoStrmWrite(hVfsIosStdOut, "Our:\n", sizeof("Our:\n") - 1, true, NULL);
        RTManifestWriteStandard(hOurManifest, hVfsIosStdOut);
        RTVfsIoStrmWrite(hVfsIosStdOut, "Their:\n", sizeof("Their:\n") - 1, true, NULL);
        RTManifestWriteStandard(hTheirManifest, hVfsIosStdOut);
#endif
    }
    else
        vboxExtPackSetError(pszError, cbError, "Error parsing '%s': %Rrc", VBOX_EXTPACK_MANIFEST_NAME, rc);

    RTManifestRelease(hTheirManifest);
    return rc;
}
示例#2
0
/**
 * Validates the extension pack tarball prior to unpacking.
 *
 * Operations performed:
 *      - Mandatory files.
 *      - Manifest check.
 *      - Manifest seal check.
 *      - XML check, match name.
 *
 * @returns VBox status code, failures with message.
 * @param   hTarballFile        The handle to open the @a pszTarball file.
 * @param   pszExtPackName      The name of the extension pack name.  NULL if
 *                              the name is not fixed.
 * @param   pszTarball          The name of the tarball in case we have to
 *                              complain about something.
 * @param   pszTarballDigest    The SHA-256 digest of the tarball.  Empty string
 *                              if no digest available.
 * @param   pszError            Where to store an error message on failure.
 * @param   cbError             The size of the buffer @a pszError points to.
 * @param   phValidManifest     Where to optionally return the handle to fully
 *                              validated the manifest for the extension pack.
 *                              This includes all files.
 * @param   phXmlFile           Where to optionally return the memorized XML
 *                              file.
 * @param   pStrDigest          Where to return the digest of the file.
 *                              Optional.
 */
int VBoxExtPackValidateTarball(RTFILE hTarballFile, const char *pszExtPackName,
                               const char *pszTarball, const char *pszTarballDigest,
                               char *pszError, size_t cbError,
                               PRTMANIFEST phValidManifest, PRTVFSFILE phXmlFile, RTCString *pStrDigest)
{
    /*
     * Clear return values.
     */
    if (phValidManifest)
        *phValidManifest = NIL_RTMANIFEST;
    if (phXmlFile)
        *phXmlFile = NIL_RTVFSFILE;
    Assert(cbError > 1);
    *pszError = '\0';
    NOREF(pszTarball);

    /*
     * Open the tar.gz filesystem stream and set up an manifest in-memory file.
     */
    RTMANIFEST      hFileManifest;
    RTVFSFSSTREAM   hTarFss;
    int rc = VBoxExtPackOpenTarFss(hTarballFile, pszError, cbError, &hTarFss, &hFileManifest);
    if (RT_FAILURE(rc))
        return rc;

    RTMANIFEST hOurManifest;
    rc = RTManifestCreate(0 /*fFlags*/, &hOurManifest);
    if (RT_SUCCESS(rc))
    {
        /*
         * Process the tarball (would be nice to move this to a function).
         */
        RTVFSFILE hXmlFile       = NIL_RTVFSFILE;
        RTVFSFILE hManifestFile  = NIL_RTVFSFILE;
        RTVFSFILE hSignatureFile = NIL_RTVFSFILE;
        for (;;)
        {
            /*
             * Get the next stream object.
             */
            char           *pszName;
            RTVFSOBJ        hVfsObj;
            RTVFSOBJTYPE    enmType;
            rc = RTVfsFsStrmNext(hTarFss, &pszName, &enmType, &hVfsObj);
            if (RT_FAILURE(rc))
            {
                if (rc != VERR_EOF)
                    vboxExtPackSetError(pszError, cbError, "RTVfsFsStrmNext failed: %Rrc", rc);
                else
                    rc = VINF_SUCCESS;
                break;
            }
            const char     *pszAdjName = pszName[0] == '.' && pszName[1] == '/' ? &pszName[2] : pszName;

            /*
             * Check the type & name validity, performing special tests on
             * standard extension pack member files.
             *
             * N.B. We will always reach the end of the loop before breaking on
             *      failure - cleanup reasons.
             */
            rc = VBoxExtPackValidateMember(pszName, enmType, hVfsObj, pszError, cbError);
            if (RT_SUCCESS(rc))
            {
                PRTVFSFILE phVfsFile = NULL;
                if (!strcmp(pszAdjName, VBOX_EXTPACK_DESCRIPTION_NAME))
                    phVfsFile = &hXmlFile;
                else if (!strcmp(pszAdjName, VBOX_EXTPACK_MANIFEST_NAME))
                    phVfsFile = &hManifestFile;
                else if (!strcmp(pszAdjName, VBOX_EXTPACK_SIGNATURE_NAME))
                    phVfsFile = &hSignatureFile;
                else if (!strncmp(pszAdjName, VBOX_EXTPACK_LICENSE_NAME_PREFIX, sizeof(VBOX_EXTPACK_LICENSE_NAME_PREFIX) - 1))
                    rc = VBoxExtPackValidateStandardFile(pszAdjName, enmType, &hVfsObj, NULL, pszError, cbError);
                if (phVfsFile)
                    rc = VBoxExtPackValidateStandardFile(pszAdjName, enmType, &hVfsObj, phVfsFile, pszError, cbError);
            }

            /*
             * Add any I/O stream to the manifest
             */
            if (   RT_SUCCESS(rc)
                && (   enmType == RTVFSOBJTYPE_FILE
                    || enmType == RTVFSOBJTYPE_IO_STREAM))
            {
                RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
                rc = RTManifestEntryAddIoStream(hOurManifest, hVfsIos, pszAdjName, RTMANIFEST_ATTR_SIZE | RTMANIFEST_ATTR_SHA256);
                if (RT_FAILURE(rc))
                    vboxExtPackSetError(pszError, cbError, "RTManifestEntryAddIoStream failed on '%s': %Rrc", pszAdjName, rc);
                RTVfsIoStrmRelease(hVfsIos);
            }

            /*
             * Clean up and break out on failure.
             */
            RTVfsObjRelease(hVfsObj);
            RTStrFree(pszName);
            if (RT_FAILURE(rc))
                break;
        }

        /*
         * Check the integrity of the tarball file.
         */
        if (RT_SUCCESS(rc))
        {
            RTVfsFsStrmRelease(hTarFss);
            hTarFss = NIL_RTVFSFSSTREAM;
            rc = vboxExtPackVerifyFileDigest(hFileManifest, pszTarballDigest, pStrDigest, pszError, cbError);
        }

        /*
         * If we've successfully processed the tarball, verify that the
         * mandatory files are present.
         */
        if (RT_SUCCESS(rc))
        {
            if (hXmlFile == NIL_RTVFSFILE)
                rc = vboxExtPackReturnError(VERR_MISSING, pszError, cbError, "Mandator file '%s' is missing",
                                            VBOX_EXTPACK_DESCRIPTION_NAME);
            if (hManifestFile == NIL_RTVFSFILE)
                rc = vboxExtPackReturnError(VERR_MISSING, pszError, cbError, "Mandator file '%s' is missing",
                                            VBOX_EXTPACK_MANIFEST_NAME);
            if (hSignatureFile == NIL_RTVFSFILE)
                rc = vboxExtPackReturnError(VERR_MISSING, pszError, cbError, "Mandator file '%s' is missing",
                                            VBOX_EXTPACK_SIGNATURE_NAME);
        }

        /*
         * Check the manifest and it's signature.
         */
        if (RT_SUCCESS(rc))
            rc = vboxExtPackVerifyManifestAndSignature(hOurManifest, hManifestFile, hSignatureFile, pszError, cbError);

        /*
         * Check the XML.
         */
        if (RT_SUCCESS(rc))
            rc = vboxExtPackVerifyXml(hXmlFile, pszExtPackName, pszError, cbError);

        /*
         * Returns objects.
         */
        if (RT_SUCCESS(rc))
        {
            if (phValidManifest)
            {
                RTManifestRetain(hOurManifest);
                *phValidManifest = hOurManifest;
            }
            if (phXmlFile)
            {
                RTVfsFileRetain(hXmlFile);
                *phXmlFile = hXmlFile;
            }
        }

        /*
         * Release our object references.
         */
        RTManifestRelease(hOurManifest);
        RTVfsFileRelease(hXmlFile);
        RTVfsFileRelease(hManifestFile);
        RTVfsFileRelease(hSignatureFile);
    }
    else
        vboxExtPackSetError(pszError, cbError, "RTManifestCreate failed: %Rrc", rc);
    RTVfsFsStrmRelease(hTarFss);
    RTManifestRelease(hFileManifest);

    return rc;
}
/**
 * Verify a manifest.
 *
 * @returns Program exit code, failures with error message.
 * @param   pszManifest         The manifest file. NULL if standard input.
 * @param   fStdFormat          Whether to expect standard format (true) or
 *                              java format (false).
 * @param   pszChDir            The directory to change into before processing
 *                              the files in the manifest.
 */
static RTEXITCODE rtManifestDoVerify(const char *pszManifest, bool fStdFormat, const char *pszChDir)
{
    /*
     * Open the manifest.
     */
    int             rc;
    RTVFSIOSTREAM   hVfsIos;
    if (!pszManifest)
    {
        rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_INPUT, RTFILE_O_READ, false /*fLeaveOpen*/, &hVfsIos);
        if (RT_FAILURE(rc))
            return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to prepare standard input for reading: %Rrc", rc);
    }
    else
    {
        const char *pszError;
        rc = RTVfsChainOpenIoStream(pszManifest, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN, &hVfsIos, &pszError);
        if (RT_FAILURE(rc))
        {
            if (pszError && *pszError)
                return RTMsgErrorExit(RTEXITCODE_FAILURE,
                                      "RTVfsChainOpenIoStream failed with rc=%Rrc:\n"
                                      "    '%s'\n",
                                      "     %*s^\n",
                                      rc, pszManifest, pszError - pszManifest, "");
            return RTMsgErrorExit(RTEXITCODE_FAILURE,
                                  "Failed with %Rrc opening the input manifest '%s'", rc, pszManifest);
        }
    }

    /*
     * Read it.
     */
    RTMANIFEST hManifest;
    rc = RTManifestCreate(0 /*fFlags*/, &hManifest);
    if (RT_SUCCESS(rc))
    {
        if (fStdFormat)
        {
            char szErr[4096 + 1024];
            rc = RTManifestReadStandardEx(hManifest, hVfsIos, szErr, sizeof(szErr));
            if (RT_SUCCESS(rc))
            {
                RTVfsIoStrmRelease(hVfsIos);
                hVfsIos = NIL_RTVFSIOSTREAM;

                /*
                 * Do the verification.
                 */
                /** @todo We're missing some enumeration APIs here! */
                RTMsgError("The manifest read fine, but the actual verification code is yet to be written. Sorry.");
                rc = VERR_NOT_IMPLEMENTED;
#if 1 /* For now, just write the manifest to stdout so we can test the read routine. */
                RTVFSIOSTREAM hVfsIosOut;
                rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE, false /*fLeaveOpen*/, &hVfsIosOut);
                if (RT_SUCCESS(rc))
                {
                    RTManifestWriteStandard(hManifest, hVfsIosOut);
                    RTVfsIoStrmRelease(hVfsIosOut);
                }
#endif
            }
            else if (szErr[0])
                RTMsgError("Error reading manifest: %s", szErr);
            else
                RTMsgError("Error reading manifest: %Rrc", rc);
        }
        else
        {
            RTMsgError("Support for Java manifest files is not implemented yet");
            rc = VERR_NOT_IMPLEMENTED;
        }
        RTManifestRelease(hManifest);
    }

    RTVfsIoStrmRelease(hVfsIos);
    return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
}
示例#4
0
/**
 * Rewinds the tarball file handle and creates a gunzip | tar chain that
 * results in a filesystem stream.
 *
 * @returns VBox status code, failures with message.
 * @param   hTarballFile        The handle to the tarball file.
 * @param   pszError            Where to store an error message on failure.
 * @param   cbError             The size of the buffer @a pszError points to.
 * @param   phTarFss            Where to return the filesystem stream handle.
 * @param   phFileManifest      Where to return a manifest where the tarball is
 *                              gettting hashed.  The entry will be called
 *                              "extpack" and be ready when the file system
 *                              stream is at an end.  Optional.
 */
int VBoxExtPackOpenTarFss(RTFILE hTarballFile, char *pszError, size_t cbError, PRTVFSFSSTREAM phTarFss,
                          PRTMANIFEST phFileManifest)
{
    Assert(cbError > 0);
    *pszError = '\0';
    *phTarFss = NIL_RTVFSFSSTREAM;

    /*
     * Rewind the file and set up a VFS chain for it.
     */
    int rc = RTFileSeek(hTarballFile, 0, RTFILE_SEEK_BEGIN, NULL);
    if (RT_FAILURE(rc))
        return vboxExtPackReturnError(rc, pszError, cbError, "Failed seeking to the start of the tarball: %Rrc", rc);

    RTVFSIOSTREAM hTarballIos;
    rc = RTVfsIoStrmFromRTFile(hTarballFile, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN, true /*fLeaveOpen*/,
                               &hTarballIos);
    if (RT_FAILURE(rc))
        return vboxExtPackReturnError(rc, pszError, cbError, "RTVfsIoStrmFromRTFile failed: %Rrc", rc);

    RTMANIFEST hFileManifest = NIL_RTMANIFEST;
    rc = RTManifestCreate(0 /*fFlags*/, &hFileManifest);
    if (RT_SUCCESS(rc))
    {
        RTVFSIOSTREAM hPtIos;
        rc = RTManifestEntryAddPassthruIoStream(hFileManifest, hTarballIos, "extpack", RTMANIFEST_ATTR_SHA256,
                                                true /*read*/, &hPtIos);
        if (RT_SUCCESS(rc))
        {
            RTVFSIOSTREAM hGunzipIos;
            rc = RTZipGzipDecompressIoStream(hPtIos, 0 /*fFlags*/, &hGunzipIos);
            if (RT_SUCCESS(rc))
            {
                RTVFSFSSTREAM hTarFss;
                rc = RTZipTarFsStreamFromIoStream(hGunzipIos, 0 /*fFlags*/, &hTarFss);
                if (RT_SUCCESS(rc))
                {
                    RTVfsIoStrmRelease(hPtIos);
                    RTVfsIoStrmRelease(hGunzipIos);
                    RTVfsIoStrmRelease(hTarballIos);
                    *phTarFss = hTarFss;
                    if (phFileManifest)
                        *phFileManifest = hFileManifest;
                    else
                        RTManifestRelease(hFileManifest);
                    return VINF_SUCCESS;
                }

                vboxExtPackSetError(pszError, cbError, "RTZipTarFsStreamFromIoStream failed: %Rrc", rc);
                RTVfsIoStrmRelease(hGunzipIos);
            }
            else
                vboxExtPackSetError(pszError, cbError, "RTZipGzipDecompressIoStream failed: %Rrc", rc);
            RTVfsIoStrmRelease(hPtIos);
        }
        else
            vboxExtPackSetError(pszError, cbError, "RTManifestEntryAddPassthruIoStream failed: %Rrc", rc);
        RTManifestRelease(hFileManifest);
    }
    else
        vboxExtPackSetError(pszError, cbError, "RTManifestCreate failed: %Rrc", rc);

    RTVfsIoStrmRelease(hTarballIos);
    return rc;
}
/**
 * Create a manifest from the specified input files.
 *
 * @returns Program exit code, failures with error message.
 * @param   pszManifest         The name of the output manifest file.  NULL if
 *                              it should be written to standard output.
 * @param   fStdFormat          Whether to expect standard format (true) or
 *                              java format (false).
 * @param   pszChDir            The directory to change into before processing
 *                              the file arguments.
 * @param   fAttr               The file attributes to put in the manifest.
 * @param   pGetState           The RTGetOpt state.
 * @param   pUnion              What the last RTGetOpt() call returned.
 * @param   chOpt               What the last RTGetOpt() call returned.
 */
static RTEXITCODE rtManifestDoCreate(const char *pszManifest, bool fStdFormat, const char *pszChDir, uint32_t fAttr,
                                     PRTGETOPTSTATE pGetState, PRTGETOPTUNION pUnion, int chOpt)
{
    /*
     * Open the manifest file.
     */
    int rc;
    RTVFSIOSTREAM hVfsIos;
    if (!pszManifest)
    {
        rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE, false /*fLeaveOpen*/, &hVfsIos);
        if (RT_FAILURE(rc))
            return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to prepare standard output for writing: %Rrc", rc);
    }
    else
    {
        const char *pszError;
        rc = RTVfsChainOpenIoStream(pszManifest, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
                                    &hVfsIos, &pszError);
        if (RT_FAILURE(rc))
        {
            if (pszError && *pszError)
                return RTMsgErrorExit(RTEXITCODE_FAILURE,
                                      "RTVfsChainOpenIoStream failed with rc=%Rrc:\n"
                                      "    '%s'\n",
                                      "     %*s^\n",
                                      rc, pszManifest, pszError - pszManifest, "");
            return RTMsgErrorExit(RTEXITCODE_FAILURE,
                                  "Failed with %Rrc opening the manifest '%s'", rc, pszManifest);
        }
    }

    /*
     * Create the internal manifest.
     */
    RTMANIFEST hManifest;
    rc = RTManifestCreate(0 /*fFlags*/, &hManifest);
    if (RT_SUCCESS(rc))
    {
        /*
         * Change directory and start processing the specified files.
         */
        if (pszChDir)
        {
            rc = RTPathSetCurrent(pszChDir);
            if (RT_FAILURE(rc))
                RTMsgError("Failed to change directory to '%s': %Rrc", pszChDir, rc);
        }
        if (RT_SUCCESS(rc))
        {
            while (chOpt == VINF_GETOPT_NOT_OPTION)
            {
                rc = rtManifestAddFileToManifest(hManifest, pUnion->psz, fAttr);
                if (RT_FAILURE(rc))
                    break;

                /* next */
                chOpt = RTGetOpt(pGetState, pUnion);
            }
            if (RT_SUCCESS(rc) && chOpt != 0)
            {
                RTGetOptPrintError(chOpt, pUnion);
                rc = chOpt < 0 ? chOpt : -chOpt;
            }
        }

        /*
         * Write the manifest.
         */
        if (RT_SUCCESS(rc))
        {
            if (fStdFormat)
            {
                rc = RTManifestWriteStandard(hManifest, hVfsIos);
                if (RT_FAILURE(rc))
                    RTMsgError("RTManifestWriteStandard failed: %Rrc", rc);
            }
            else
            {
                RTMsgError("Support for Java manifest files is not implemented yet");
                rc = VERR_NOT_IMPLEMENTED;
            }
        }

        RTManifestRelease(hManifest);
    }

    RTVfsIoStrmRelease(hVfsIos);
    return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
}