VirtualBox

Changeset 94726 in vbox


Ignore:
Timestamp:
Apr 27, 2022 5:39:33 PM (3 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
151106
Message:

Main/Settings: Implement encrypting/decrypting a VM config, bugref:9955

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/VBox/settings.h

    r94714 r94726  
    5050#include <VBox/com/Guid.h>
    5151#include <VBox/com/string.h>
     52#include <VBox/VBoxCryptoIf.h>
    5253
    5354#include <list>
     
    618619
    619620    com::Utf8Str    strNvramPath;
     621    com::Utf8Str    strKeyId;
     622    com::Utf8Str    strKeyStore;
    620623};
    621624
     
    13681371    com::Guid               uuid;
    13691372
     1373    enum
     1374    {
     1375        ParseState_NotParsed,
     1376        ParseState_PasswordError,
     1377        ParseState_Parsed
     1378    }                       enmParseState;
     1379
    13701380    MachineUserData         machineUserData;
    13711381
     1382    com::Utf8Str            strStateKeyId;
     1383    com::Utf8Str            strStateKeyStore;
    13721384    com::Utf8Str            strStateFile;
    13731385    bool                    fCurrentStateModified;      // optional, default is true
     
    13861398    SnapshotsList           llFirstSnapshot;            // first snapshot or empty list if there's none
    13871399
    1388     MachineConfigFile(const com::Utf8Str *pstrFilename);
     1400    com::Utf8Str            strKeyId;
     1401    com::Utf8Str            strKeyStore;                // if not empty, the encryption is used
     1402    com::Utf8Str            strLogKeyId;
     1403    com::Utf8Str            strLogKeyStore;
     1404
     1405    MachineConfigFile(const com::Utf8Str *pstrFilename,
     1406                      PCVBOXCRYPTOIF pCryptoIf = NULL,
     1407                      const char *pszPassword = NULL);
    13891408
    13901409    bool operator==(const MachineConfigFile &m) const;
     
    13941413    void importMachineXML(const xml::ElementNode &elmMachine);
    13951414
    1396     void write(const com::Utf8Str &strFilename);
     1415    void write(const com::Utf8Str &strFilename, PCVBOXCRYPTOIF pCryptoIf = NULL, const char *pszPassword = NULL);
    13971416
    13981417    enum
     
    14041423        BuildMachineXML_SuppressSavedState = 0x10
    14051424    };
     1425
     1426    void copyEncryptionSettingsFrom(const MachineConfigFile &other);
    14061427    void buildMachineXML(xml::ElementNode &elmMachine,
    14071428                         uint32_t fl,
     
    14321453    void convertOldOSType_pre1_5(com::Utf8Str &str);
    14331454    void readMachine(const xml::ElementNode &elmMachine);
     1455    void readMachineEncrypted(const xml::ElementNode &elmMachine, PCVBOXCRYPTOIF pCryptoIf, const char *pszPassword);
    14341456
    14351457    void buildHardwareXML(xml::ElementNode &elmParent, const Hardware &hw, uint32_t fl, std::list<xml::ElementNode*> *pllElementsWithUuidAttributes);
     
    14441466    void buildSnapshotXML(xml::ElementNode &elmParent, const Snapshot &snap);
    14451467
     1468    void buildMachineEncryptedXML(xml::ElementNode &elmMachine,
     1469                                  uint32_t fl,
     1470                                  std::list<xml::ElementNode*> *pllElementsWithUuidAttributes,
     1471                                  PCVBOXCRYPTOIF pCryptoIf,
     1472                                  const char *pszPassword);
     1473
    14461474    void bumpSettingsVersionIfNeeded();
    14471475};
  • trunk/src/VBox/Main/xml/Settings.cpp

    r94714 r94726  
    30263026bool NvramSettings::areDefaultSettings() const
    30273027{
    3028     return strNvramPath.isEmpty();
     3028    return     strNvramPath.isEmpty()
     3029            && strKeyId.isEmpty()
     3030            && strKeyStore.isEmpty();
    30293031}
    30303032
     
    30373039{
    30383040    return (this == &g)
    3039         || (strNvramPath    == g.strNvramPath);
     3041        || (strNvramPath    == g.strNvramPath)
     3042        || (strKeyId        == g.strKeyId)
     3043        || (strKeyStore     == g.strKeyStore);
    30403044}
    30413045
     
    39943998 *
    39953999 * @param pstrFilename
    3996  */
    3997 MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
     4000 * @param pCryptoIf             Pointer to the cryptographic interface, required for an encrypted machine config.
     4001 * @param pszPassword           The password to use for an encrypted machine config.
     4002 */
     4003MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename, PCVBOXCRYPTOIF pCryptoIf, const char *pszPassword)
    39984004    : ConfigFileBase(pstrFilename),
     4005      enmParseState(ParseState_NotParsed),
    39994006      fCurrentStateModified(true),
    40004007      fAborted(false)
     
    40114018        while ((pelmRootChild = nlRootChildren.forAllNodes()))
    40124019        {
     4020            if (pelmRootChild->nameEquals("MachineEncrypted"))
     4021                readMachineEncrypted(*pelmRootChild, pCryptoIf, pszPassword);
    40134022            if (pelmRootChild->nameEquals("Machine"))
    40144023                readMachine(*pelmRootChild);
     
    40174026        // clean up memory allocated by XML engine
    40184027        clearDocument();
     4028
     4029        if (enmParseState == ParseState_NotParsed)
     4030            enmParseState = ParseState_Parsed;
    40194031    }
    40204032}
     
    40294041{
    40304042    return (m->sv >= SettingsVersion_v1_11);
     4043}
     4044
     4045/**
     4046 * Public routine which copies encryption settings. Used by Machine::saveSettings
     4047 * so that the encryption settings do not get lost when a copy of the Machine settings
     4048 * file is made to see if settings have actually changed.
     4049 * @param other
     4050 */
     4051void MachineConfigFile::copyEncryptionSettingsFrom(const MachineConfigFile &other)
     4052{
     4053    strKeyId    = other.strKeyId;
     4054    strKeyStore = other.strKeyStore;
    40314055}
    40324056
     
    40894113            && mediaRegistry              == c.mediaRegistry        // this one's deep
    40904114            // skip mapExtraDataItems! there is no old state available as it's always forced
    4091             && llFirstSnapshot            == c.llFirstSnapshot);    // this one's deep
     4115            && llFirstSnapshot            == c.llFirstSnapshot     // this one's deep
     4116            && strKeyId                   == c.strKeyId
     4117            && strKeyStore                == c.strKeyStore
     4118            && strStateKeyId              == c.strStateKeyId
     4119            && strStateKeyStore           == c.strStateKeyStore
     4120            && strLogKeyId                == c.strLogKeyId
     4121            && strLogKeyStore             == c.strLogKeyStore);
    40924122}
    40934123
     
    51695199                pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset);
    51705200            if ((pelmBIOSChild = pelmHwChild->findChildElement("NVRAM")))
     5201            {
    51715202                pelmBIOSChild->getAttributeValue("path", hw.nvramSettings.strNvramPath);
     5203                if (m->sv >= SettingsVersion_v1_19)
     5204                {
     5205                    pelmBIOSChild->getAttributeValue("keyId", hw.nvramSettings.strKeyId);
     5206                    pelmBIOSChild->getAttributeValue("keyStore", hw.nvramSettings.strKeyStore);
     5207                }
     5208            }
    51725209            if ((pelmBIOSChild = pelmHwChild->findChildElement("SmbiosUuidLittleEndian")))
    51735210                pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fSmbiosUuidLittleEndian);
     
    60986135            convertOldOSType_pre1_5(machineUserData.strOsType);
    60996136
     6137        elmMachine.getAttributeValue("stateKeyId", strStateKeyId);
     6138        elmMachine.getAttributeValue("stateKeyStore", strStateKeyStore);
    61006139        elmMachine.getAttributeValuePath("stateFile", strStateFile);
     6140
     6141        elmMachine.getAttributeValue("strLogKeyId", strLogKeyId);
     6142        elmMachine.getAttributeValue("strLogKeyStore", strLogKeyStore);
    61016143
    61026144        if (elmMachine.getAttributeValue("currentSnapshot", str))
     
    61936235
    61946236/**
     6237 * Called from the constructor to decrypt the machine config and read
     6238 * data from it.
     6239 * @param elmMachine
     6240 */
     6241void MachineConfigFile::readMachineEncrypted(const xml::ElementNode &elmMachine,
     6242                                             PCVBOXCRYPTOIF pCryptoIf = NULL,
     6243                                             const char *pszPassword = NULL)
     6244{
     6245    Utf8Str strUUID;
     6246    if (elmMachine.getAttributeValue("uuid", strUUID))
     6247    {
     6248        parseUUID(uuid, strUUID, &elmMachine);
     6249        if (!elmMachine.getAttributeValue("keyId", strKeyId))
     6250            throw ConfigFileError(this, &elmMachine, N_("Required MachineEncrypted/@keyId attribute is missing"));
     6251        if (!elmMachine.getAttributeValue("keyStore", strKeyStore))
     6252            throw ConfigFileError(this, &elmMachine, N_("Required MachineEncrypted/@keyStore attribute is missing"));
     6253
     6254        if (!pszPassword)
     6255        {
     6256            enmParseState = ParseState_PasswordError;
     6257            return;
     6258        }
     6259
     6260        VBOXCRYPTOCTX hCryptoCtx = NULL;
     6261        int rc = pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), pszPassword, &hCryptoCtx);
     6262        if (RT_SUCCESS(rc))
     6263        {
     6264            com::Utf8Str str = elmMachine.getValue();
     6265            IconBlob abEncrypted; /** @todo Rename IconBlob because this is not about icons. */
     6266            /** @todo This is not nice. */
     6267            try
     6268            {
     6269                parseBase64(abEncrypted, str, &elmMachine);
     6270            }
     6271            catch(...)
     6272            {
     6273                int rc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
     6274                AssertRC(rc2);
     6275                throw;
     6276            }
     6277
     6278            IconBlob abDecrypted(abEncrypted.size());
     6279            size_t cbDecrypted = 0;
     6280            rc = pCryptoIf->pfnCryptoCtxDecrypt(hCryptoCtx, false /*fPartial*/,
     6281                                                &abEncrypted[0], abEncrypted.size(),
     6282                                                uuid.raw(), sizeof(RTUUID),
     6283                                                &abDecrypted[0], abDecrypted.size(), &cbDecrypted);
     6284            int rc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
     6285            AssertRC(rc2);
     6286
     6287            if (RT_SUCCESS(rc))
     6288            {
     6289                abDecrypted.resize(cbDecrypted);
     6290                xml::XmlMemParser parser;
     6291                xml::Document *pDoc = new xml::Document;
     6292                parser.read(&abDecrypted[0], abDecrypted.size(), m->strFilename, *pDoc);
     6293                xml::ElementNode *pelmRoot = pDoc->getRootElement();
     6294                if (!pelmRoot || !pelmRoot->nameEquals("Machine"))
     6295                    throw ConfigFileError(this, pelmRoot, N_("Root element in Machine settings encrypted block must be \"Machine\""));
     6296                readMachine(*pelmRoot);
     6297                delete pDoc;
     6298            }
     6299        }
     6300
     6301        if (RT_FAILURE(rc))
     6302        {
     6303            if (rc == VERR_ACCESS_DENIED)
     6304                enmParseState = ParseState_PasswordError;
     6305            else
     6306                throw ConfigFileError(this, &elmMachine, N_("Parsing config failed. (%Rrc)"), rc);
     6307        }
     6308    }
     6309    else
     6310        throw ConfigFileError(this, &elmMachine, N_("Required MachineEncrypted/@uuid attribute is missing"));
     6311}
     6312
     6313/**
    61956314 * Creates a \<Hardware\> node under elmParent and then writes out the XML
    61966315 * keys under that. Called for both the \<Machine\> node and for snapshots.
     
    66646783    }
    66656784
    6666     if (!hw.biosSettings.areDefaultSettings())
     6785    if (!hw.biosSettings.areDefaultSettings() || !hw.nvramSettings.areDefaultSettings())
    66676786    {
    66686787        xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
     
    67186837        if (hw.biosSettings.fPXEDebugEnabled)
    67196838            pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
    6720         if (!hw.nvramSettings.strNvramPath.isEmpty())
    6721             pelmBIOS->createChild("NVRAM")->setAttribute("path", hw.nvramSettings.strNvramPath);
     6839        if (!hw.nvramSettings.areDefaultSettings())
     6840        {
     6841            xml::ElementNode *pelmNvram = pelmBIOS->createChild("NVRAM");
     6842            if (!hw.nvramSettings.strNvramPath.isEmpty())
     6843                pelmNvram->setAttribute("path", hw.nvramSettings.strNvramPath);
     6844            if (m->sv >= SettingsVersion_v1_9)
     6845            {
     6846                if (hw.nvramSettings.strKeyId.isNotEmpty())
     6847                    pelmNvram->setAttribute("keyId", hw.nvramSettings.strKeyId);
     6848                if (hw.nvramSettings.strKeyStore.isNotEmpty())
     6849                    pelmNvram->setAttribute("keyStore", hw.nvramSettings.strKeyStore);
     6850            }
     6851        }
    67226852        if (hw.biosSettings.fSmbiosUuidLittleEndian)
    67236853            pelmBIOS->createChild("SmbiosUuidLittleEndian")->setAttribute("enabled", hw.biosSettings.fSmbiosUuidLittleEndian);
     
    79018031        elmMachine.createChild("Description")->addContent(machineUserData.strDescription);
    79028032    elmMachine.setAttribute("OSType", machineUserData.strOsType);
     8033
     8034
     8035    if (m->sv >= SettingsVersion_v1_19)
     8036    {
     8037        if (strStateKeyId.length())
     8038            elmMachine.setAttribute("stateKeyId", strStateKeyId);
     8039        if (strStateKeyStore.length())
     8040            elmMachine.setAttribute("stateKeyStore", strStateKeyStore);
     8041        if (strLogKeyId.length())
     8042            elmMachine.setAttribute("strLogKeyId", strLogKeyId);
     8043        if (strLogKeyStore.length())
     8044            elmMachine.setAttribute("strLogKeyStore", strLogKeyStore);
     8045    }
    79038046    if (    strStateFile.length()
    79048047         && !(fl & BuildMachineXML_SuppressSavedState)
     
    79748117    buildAutostartXML(elmMachine, autostart);
    79758118    buildGroupsXML(elmMachine, machineUserData.llGroups);
     8119}
     8120
     8121 /**
     8122 * Builds encrypted config.
     8123 *
     8124 * @sa MachineConfigFile::buildMachineXML
     8125 */
     8126void MachineConfigFile::buildMachineEncryptedXML(xml::ElementNode &elmMachine,
     8127                                                 uint32_t fl,
     8128                                                 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes,
     8129                                                 PCVBOXCRYPTOIF pCryptoIf,
     8130                                                 const char *pszPassword = NULL)
     8131{
     8132    if (   !pszPassword
     8133        || !pCryptoIf)
     8134        throw ConfigFileError(this, &elmMachine, N_("Password is required"));
     8135
     8136    xml::Document *pDoc = new xml::Document;
     8137    xml::ElementNode *pelmRoot = pDoc->createRootElement("Machine");
     8138    pelmRoot->setAttribute("xmlns", VBOX_XML_NAMESPACE);
     8139    // Have the code for producing a proper schema reference. Not used by most
     8140    // tools, so don't bother doing it. The schema is not on the server anyway.
     8141#ifdef VBOX_WITH_SETTINGS_SCHEMA
     8142    pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
     8143    pelmRoot->setAttribute("xsi:schemaLocation", VBOX_XML_NAMESPACE " " VBOX_XML_SCHEMA);
     8144#endif
     8145
     8146    buildMachineXML(*pelmRoot, fl, pllElementsWithUuidAttributes);
     8147    xml::XmlStringWriter writer;
     8148    com::Utf8Str strMachineXml;
     8149    int rc = writer.write(*pDoc, &strMachineXml);
     8150    delete pDoc;
     8151    if (RT_SUCCESS(rc))
     8152    {
     8153        VBOXCRYPTOCTX hCryptoCtx;
     8154        if (strKeyStore.isEmpty())
     8155        {
     8156            rc = pCryptoIf->pfnCryptoCtxCreate("AES-GCM256", pszPassword, &hCryptoCtx);
     8157            if (RT_SUCCESS(rc))
     8158            {
     8159                char *pszNewKeyStore;
     8160                rc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszNewKeyStore);
     8161                if (RT_SUCCESS(rc))
     8162                {
     8163                    strKeyStore = pszNewKeyStore;
     8164                    RTStrFree(pszNewKeyStore);
     8165                }
     8166                else
     8167                    pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
     8168            }
     8169        }
     8170        else
     8171            rc = pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), pszPassword, &hCryptoCtx);
     8172        if (RT_SUCCESS(rc))
     8173        {
     8174            IconBlob abEncrypted;
     8175            size_t cbEncrypted = 0;
     8176            rc = pCryptoIf->pfnCryptoCtxQueryEncryptedSize(hCryptoCtx, strMachineXml.length(), &cbEncrypted);
     8177            if (RT_SUCCESS(rc))
     8178            {
     8179                abEncrypted.resize(cbEncrypted);
     8180                rc = pCryptoIf->pfnCryptoCtxEncrypt(hCryptoCtx, false /*fPartial*/, NULL /*pvIV*/, 0 /*cbIV*/,
     8181                                                    strMachineXml.c_str(), strMachineXml.length(),
     8182                                                    uuid.raw(), sizeof(RTUUID),
     8183                                                    &abEncrypted[0], abEncrypted.size(), &cbEncrypted);
     8184                int rc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
     8185                AssertRC(rc2);
     8186                if (RT_SUCCESS(rc))
     8187                {
     8188                    abEncrypted.resize(cbEncrypted);
     8189                    toBase64(strMachineXml, abEncrypted);
     8190                    elmMachine.setAttribute("uuid", uuid.toStringCurly());
     8191                    elmMachine.setAttribute("keyId", strKeyId);
     8192                    elmMachine.setAttribute("keyStore", strKeyStore);
     8193                    elmMachine.setContent(strMachineXml.c_str());
     8194                }
     8195            }
     8196        }
     8197
     8198        if (RT_FAILURE(rc))
     8199            throw ConfigFileError(this, &elmMachine, N_("Creating machine encrypted xml failed. (%Rrc)"), rc);
     8200    }
     8201    else
     8202        throw ConfigFileError(this, &elmMachine, N_("Creating machine xml failed. (%Rrc)"), rc);
    79768203}
    79778204
     
    80808307    if (m->sv < SettingsVersion_v1_19)
    80818308    {
    8082         // VirtualBox 7.0 adds iommu device.
    8083         if (hardwareMachine.iommuType != IommuType_None)
     8309        // VirtualBox 7.0 adds iommu device and full VM encryption.
     8310        if (   hardwareMachine.iommuType != IommuType_None
     8311            || strKeyId.isNotEmpty()
     8312            || strKeyStore.isNotEmpty()
     8313            || strStateKeyId.isNotEmpty()
     8314            || strStateKeyStore.isNotEmpty()
     8315            || hardwareMachine.nvramSettings.strKeyId.isNotEmpty()
     8316            || hardwareMachine.nvramSettings.strKeyStore.isNotEmpty()
     8317            || strLogKeyId.isNotEmpty()
     8318            || strLogKeyStore.isEmpty())
    80848319        {
    80858320            m->sv = SettingsVersion_v1_19;
     
    87428977 * in particular if the file cannot be written.
    87438978 */
    8744 void MachineConfigFile::write(const com::Utf8Str &strFilename)
     8979void MachineConfigFile::write(const com::Utf8Str &strFilename, PCVBOXCRYPTOIF pCryptoIf, const char *pszPassword)
    87458980{
    87468981    try
     
    87528987
    87538988        m->strFilename = strFilename;
    8754         specialBackupIfFirstBump();
     8989        /*
     8990         * Only create a backup if it is not encrypted.
     8991         * Otherwise we get an unencrypted copy of the settings.
     8992         */
     8993        if (strKeyId.isEmpty() && strKeyStore.isEmpty())
     8994            specialBackupIfFirstBump();
    87558995        createStubDocument();
    87568996
    8757         xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
    8758         buildMachineXML(*pelmMachine,
    8759                           MachineConfigFile::BuildMachineXML_IncludeSnapshots
    8760                         | MachineConfigFile::BuildMachineXML_MediaRegistry,
    8761                             // but not BuildMachineXML_WriteVBoxVersionAttribute
    8762                         NULL); /* pllElementsWithUuidAttributes */
     8997        if (strKeyStore.isNotEmpty())
     8998        {
     8999            xml::ElementNode *pelmMachine = m->pelmRoot->createChild("MachineEncrypted");
     9000            buildMachineEncryptedXML(*pelmMachine,
     9001                                       MachineConfigFile::BuildMachineXML_IncludeSnapshots
     9002                                     | MachineConfigFile::BuildMachineXML_MediaRegistry,
     9003                                         // but not BuildMachineXML_WriteVBoxVersionAttribute
     9004                                     NULL, /* pllElementsWithUuidAttributes */
     9005                                     pCryptoIf,
     9006                                     pszPassword);
     9007        }
     9008        else
     9009        {
     9010            xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
     9011            buildMachineXML(*pelmMachine,
     9012                              MachineConfigFile::BuildMachineXML_IncludeSnapshots
     9013                            | MachineConfigFile::BuildMachineXML_MediaRegistry,
     9014                                // but not BuildMachineXML_WriteVBoxVersionAttribute
     9015                            NULL); /* pllElementsWithUuidAttributes */
     9016        }
    87639017
    87649018        // now go write the XML
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