VirtualBox

Changeset 94735 in vbox for trunk/src/VBox/Main/src-server


Ignore:
Timestamp:
Apr 28, 2022 12:54:21 PM (3 years ago)
Author:
vboxsync
Message:

Main/MachineImpl: First part of the full VM encryption integration, creating an encrypted VM works now, bugref:9955

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/src-server/MachineImpl.cpp

    r94661 r94735  
    6969#include <iprt/env.h>
    7070#include <iprt/lockvalidator.h>
     71#include <iprt/memsafer.h>
    7172#include <iprt/process.h>
    7273#include <iprt/cpp/utils.h>
     
    7879#include <VBox/com/array.h>
    7980#include <VBox/com/list.h>
     81#include <VBox/VBoxCryptoIf.h>
    8082
    8183#include <VBox/err.h>
     
    110112// defines / prototypes
    111113/////////////////////////////////////////////////////////////////////////////
     114
     115#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     116# define BUF_DATA_SIZE     _64K
     117
     118enum CipherMode
     119{
     120    CipherModeGcm = 0,
     121    CipherModeCtr,
     122    CipherModeXts,
     123    CipherModeMax
     124};
     125
     126enum AesSize
     127{
     128    Aes128 = 0,
     129    Aes256,
     130    AesMax
     131};
     132
     133const char *g_apszCipher[AesMax][CipherModeMax] =
     134{
     135    {"AES-GCM128", "AES-CTR128", "AES-XTS128-PLAIN64"},
     136    {"AES-GCM256", "AES-CTR256", "AES-XTS256-PLAIN64"}
     137};
     138const char *g_apszCipherAlgo[AesMax] = {"AES-128", "AES-256"};
     139
     140static const char *getCipherString(const char *pszAlgo, const int iMode)
     141{
     142    if (iMode >= CipherModeMax)
     143        return pszAlgo;
     144
     145    for (int i = 0; i < AesMax; i++)
     146    {
     147        if (strcmp(pszAlgo, g_apszCipherAlgo[i]) == 0)
     148            return g_apszCipher[i][iMode];
     149    }
     150    return pszAlgo;
     151}
     152
     153static const char *getCipherStringWithoutMode(const char *pszAlgo)
     154{
     155    for (int i = 0; i < AesMax; i++)
     156    {
     157        for (int j = 0; j < CipherModeMax; j++)
     158        {
     159            if (strcmp(pszAlgo, g_apszCipher[i][j]) == 0)
     160                return g_apszCipherAlgo[i];
     161        }
     162    }
     163    return pszAlgo;
     164}
     165#endif
    112166
    113167/////////////////////////////////////////////////////////////////////////////
     
    144198    mSession.mLockType         = LockType_Null;
    145199    mSession.mState            = SessionState_Unlocked;
     200
     201#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     202    mpKeyStore                 = NULL;
     203#endif
    146204}
    147205
     
    310368    if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
    311369        return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
    312 #else
    313     /** @todo */
    314     RT_NOREF(aCipher, aPasswordId, aPassword);
    315370#endif
    316371
     
    322377    if (FAILED(rc)) return rc;
    323378
     379#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     380    com::Utf8Str strSsmKeyId;
     381    com::Utf8Str strSsmKeyStore;
     382    com::Utf8Str strNVRAMKeyId;
     383    com::Utf8Str strNVRAMKeyStore;
     384
     385    if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
     386    {
     387        /* Resolve the cryptographic interface. */
     388        PCVBOXCRYPTOIF pCryptoIf = NULL;
     389        HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
     390        if (SUCCEEDED(hrc))
     391        {
     392            CipherMode aenmMode[]        = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
     393            com::Utf8Str *astrKeyId[]    = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
     394            com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
     395
     396            for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
     397            {
     398                const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
     399                if (!pszCipher)
     400                {
     401                    hrc = setError(VBOX_E_NOT_SUPPORTED,
     402                                   tr("The cipher '%s' is not supported"), aCipher.c_str());
     403                    break;
     404                }
     405
     406                VBOXCRYPTOCTX hCryptoCtx;
     407                int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
     408                if (RT_FAILURE(vrc))
     409                {
     410                    hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
     411                    break;
     412                }
     413
     414                char *pszKeyStore;
     415                vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
     416                int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
     417                AssertRC(vrc2);
     418
     419                if (RT_FAILURE(vrc))
     420                {
     421                    hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
     422                    break;
     423                }
     424
     425                *(astrKeyStore[i]) = pszKeyStore;
     426                RTMemFree(pszKeyStore);
     427                *(astrKeyId[i]) = aPasswordId;
     428            }
     429
     430            HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
     431            Assert(hrc2 == S_OK);
     432
     433            if (FAILED(hrc))
     434                return hrc; /* Error is set. */
     435        }
     436        else
     437            return hrc; /* Error is set. */
     438    }
     439#endif
     440
    324441    rc = i_tryCreateMachineConfigFile(fForceOverwrite);
    325442    if (FAILED(rc)) return rc;
     
    335452    if (SUCCEEDED(rc))
    336453    {
     454#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     455        mSSData->strStateKeyId = strSsmKeyId;
     456        mSSData->strStateKeyStore = strSsmKeyStore;
     457#endif
     458
    337459        // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
    338460        mData->mAccessible = TRUE;
     
    417539    if (SUCCEEDED(rc))
    418540    {
     541#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     542        if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
     543        {
     544            size_t   cbPassword = aPassword.length() + 1;
     545            uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
     546            mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
     547        }
     548#endif
     549
    419550        if (mData->mAccessible)
    420551            autoInitSpan.setSucceeded();
     
    460591    LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
    461592
     593    PCVBOXCRYPTOIF pCryptoIf = NULL;
    462594#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
    463595    if (strPassword.isNotEmpty())
    464596        return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
    465597#else
    466     /** @todo */
    467     RT_NOREF(strPassword);
     598    if (strPassword.isNotEmpty())
     599    {
     600        /* Get at the crpytographic interface. */
     601        HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
     602        if (FAILED(hrc))
     603            return hrc; /* Error is set. */
     604    }
    468605#endif
    469606
     
    497634            {
    498635                // load and parse machine XML; this will throw on XML or logic errors
    499                 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
     636                mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
     637                                                                            pCryptoIf,
     638                                                                            strPassword.c_str());
    500639
    501640                // reject VM UUID duplicates, they can happen if someone
     
    514653                unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
    515654
    516                 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
    517                                                  NULL /* puuidRegistry */);
    518                 if (FAILED(rc)) throw rc;
    519 
    520                 /* At this point the changing of the current state modification
    521                  * flag is allowed. */
    522                 i_allowStateModification();
    523 
    524                 i_commit();
     655#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     656                // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
     657                // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
     658                mData->mstrKeyId    = mData->pMachineConfigFile->strKeyId;
     659                mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
     660#endif
     661
     662                if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
     663                {
     664                    // We just set the inaccessible state and fill the error info allowing the caller
     665                    // to register the machine with encrypted config even if the password is incorrect
     666                    mData->mAccessible = FALSE;
     667
     668                    /* fetch the current error info */
     669                    mData->mAccessError = com::ErrorInfo();
     670
     671                    throw setError(VBOX_E_PASSWORD_INCORRECT,
     672                                   tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
     673                                   mData->pMachineConfigFile->uuid.raw());
     674                }
     675                else
     676                {
     677#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     678                    if (strPassword.isNotEmpty())
     679                    {
     680                        size_t cbKey = strPassword.length() + 1; /* Include terminator */
     681                        const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
     682                        mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
     683                    }
     684#endif
     685
     686                    rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
     687                                                       NULL /* puuidRegistry */);
     688                    if (FAILED(rc)) throw rc;
     689
     690                    /* At this point the changing of the current state modification
     691                     * flag is allowed. */
     692                    i_allowStateModification();
     693
     694                    i_commit();
     695                }
    525696            }
    526697            catch (HRESULT err)
     
    550721        }
    551722    }
     723
     724#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     725    if (pCryptoIf)
     726    {
     727        HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
     728        Assert(hrc2 == S_OK);
     729    }
     730#endif
    552731
    553732    LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
     
    684863                            vrc1);
    685864
     865#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     866    /** @todo Only create when the machine is going to be encrypted. */
     867    /* Non-pageable memory is not accessible for non-VM process */
     868    mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
     869    AssertReturn(mData->mpKeyStore, VERR_NO_MEMORY);
     870#endif
     871
    686872    LogFlowThisFuncLeave();
    687873
     
    762948        mData->mRegistered = FALSE;
    763949
    764         try
    765         {
    766             // load and parse machine XML; this will throw on XML or logic errors
    767             mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
    768 
    769             if (mData->mUuid != mData->pMachineConfigFile->uuid)
    770                 throw setError(E_FAIL,
    771                                tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
    772                                mData->pMachineConfigFile->uuid.raw(),
    773                                mData->m_strConfigFileFull.c_str(),
    774                                mData->mUuid.toString().c_str(),
    775                                mParent->i_settingsFilePath().c_str());
    776 
    777             rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
    778                                                NULL /* const Guid *puuidRegistry */);
    779             if (FAILED(rc)) throw rc;
    780         }
    781         catch (HRESULT err)
    782         {
    783             /* we assume that error info is set by the thrower */
    784             rc = err;
    785         }
    786         catch (...)
    787         {
    788             rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
    789         }
    790 
    791         /* Restore the registered flag (even on failure) */
    792         mData->mRegistered = TRUE;
     950        PCVBOXCRYPTOIF pCryptoIf = NULL;
     951        SecretKey *pKey = NULL;
     952        const char *pszPassword = NULL;
     953#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     954        /* Resolve password and cryptographic support interface if machine is encrypted. */
     955        if (mData->mstrKeyId.isNotEmpty())
     956        {
     957            /* Get at the crpytographic interface. */
     958            rc = mParent->i_retainCryptoIf(&pCryptoIf);
     959            if (SUCCEEDED(rc))
     960            {
     961                int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
     962                if (RT_SUCCESS(vrc))
     963                    pszPassword = (const char *)pKey->getKeyBuffer();
     964                else
     965                    rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
     966                                      mData->mstrKeyId.c_str(), vrc);
     967            }
     968        }
     969#else
     970        RT_NOREF(pKey);
     971#endif
     972
     973        if (SUCCEEDED(rc))
     974        {
     975            try
     976            {
     977                // load and parse machine XML; this will throw on XML or logic errors
     978                mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
     979                                                                            pCryptoIf, pszPassword);
     980
     981                if (mData->mUuid != mData->pMachineConfigFile->uuid)
     982                    throw setError(E_FAIL,
     983                                   tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
     984                                   mData->pMachineConfigFile->uuid.raw(),
     985                                   mData->m_strConfigFileFull.c_str(),
     986                                   mData->mUuid.toString().c_str(),
     987                                   mParent->i_settingsFilePath().c_str());
     988
     989#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     990                // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
     991                // We fill in the encryptions fields, and the rest will be filled in if all data parsed
     992                mData->mstrKeyId    = mData->pMachineConfigFile->strKeyId;
     993                mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
     994
     995                if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
     996                    throw setError(VBOX_E_PASSWORD_INCORRECT,
     997                                   tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
     998                                   mData->pMachineConfigFile->uuid.raw());
     999#endif
     1000
     1001                rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
     1002                                                   NULL /* const Guid *puuidRegistry */);
     1003                if (FAILED(rc)) throw rc;
     1004            }
     1005            catch (HRESULT err)
     1006            {
     1007                /* we assume that error info is set by the thrower */
     1008                rc = err;
     1009            }
     1010            catch (...)
     1011            {
     1012                rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
     1013            }
     1014
     1015            /* Restore the registered flag (even on failure) */
     1016            mData->mRegistered = TRUE;
     1017        }
     1018
     1019#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     1020        if (pCryptoIf)
     1021            mParent->i_releaseCryptoIf(pCryptoIf);
     1022        if (pKey)
     1023            mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
     1024#endif
    7931025    }
    7941026
     
    9291161    if (mData->mAccessible)
    9301162        uninitDataAndChildObjects();
     1163
     1164#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     1165    if (mData->mpKeyStore != NULL)
     1166        delete mData->mpKeyStore;
     1167#endif
    9311168
    9321169    /* free the essential data structure last */
     
    1000210239
    1000310240    HRESULT rc = S_OK;
     10241    PCVBOXCRYPTOIF pCryptoIf = NULL;
     10242    const char  *pszPassword = NULL;
     10243    SecretKey   *pKey        = NULL;
     10244
     10245#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     10246    if (mData->mstrKeyId.isNotEmpty())
     10247    {
     10248        /* VM is going to be encrypted. */
     10249        alock.release(); /** @todo Revise the locking. */
     10250        rc = mParent->i_retainCryptoIf(&pCryptoIf);
     10251        alock.acquire();
     10252        if (FAILED(rc)) return rc; /* Error is set. */
     10253
     10254        int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
     10255        if (RT_SUCCESS(vrc))
     10256            pszPassword = (const char *)pKey->getKeyBuffer();
     10257        else
     10258        {
     10259            mParent->i_releaseCryptoIf(pCryptoIf);
     10260            return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
     10261                                tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
     10262                                mData->mstrKeyId.c_str(), vrc);
     10263        }
     10264    }
     10265#else
     10266    RT_NOREF(pKey);
     10267#endif
     10268
    1000410269    bool fNeedsWrite = false;
    1000510270    bool fSettingsFileIsNew = false;
     
    1001010275    rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
    1001110276                               &fSettingsFileIsNew);
    10012     if (FAILED(rc)) return rc;
     10277    if (FAILED(rc))
     10278    {
     10279#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     10280        if (pCryptoIf)
     10281        {
     10282            alock.release(); /** @todo Revise the locking. */
     10283            mParent->i_releaseCryptoIf(pCryptoIf);
     10284            alock.acquire();
     10285        }
     10286        if (pKey)
     10287            mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
     10288#endif
     10289        return rc;
     10290    }
    1001310291
    1001410292    // keep a pointer to the current settings structures
     
    1002110299        pNewConfig = new settings::MachineConfigFile(NULL);
    1002210300        pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
     10301#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     10302        pNewConfig->strKeyId    = mData->mstrKeyId;
     10303        pNewConfig->strKeyStore = mData->mstrKeyStore;
     10304#endif
    1002310305
    1002410306        // now go and copy all the settings data from COM to the settings structures
     
    1005810340
    1005910341        if (fNeedsWrite)
     10342        {
    1006010343            // now spit it all out!
    10061             pNewConfig->write(mData->m_strConfigFileFull);
     10344            pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
     10345            if (aFlags & SaveS_RemoveBackup)
     10346                RTFileDelete((mData->m_strConfigFileFull + "-prev").c_str());
     10347        }
    1006210348
    1006310349        mData->pMachineConfigFile = pNewConfig;
     
    1008510371        rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
    1008610372    }
     10373
     10374#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
     10375    if (pCryptoIf)
     10376    {
     10377        alock.release(); /** @todo Revise the locking. */
     10378        mParent->i_releaseCryptoIf(pCryptoIf);
     10379        alock.acquire();
     10380    }
     10381    if (pKey)
     10382        mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
     10383#endif
    1008710384
    1008810385    if (fNeedsWrite)
Note: See TracChangeset for help on using the changeset viewer.

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