VirtualBox

Ignore:
Timestamp:
Apr 4, 2024 5:05:37 PM (10 months ago)
Author:
vboxsync
Message:

Windows/Host Installer: Check permissions of target directory when installing. Added a new testcase for this. bugref:10616

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp

    r103889 r104162  
    3838#include <iprt/win/windows.h>
    3939
     40#include <aclapi.h>
    4041#include <msi.h>
    4142#include <msiquery.h>
     
    4647#include <cfgmgr32.h>
    4748#include <devguid.h>
     49#include <sddl.h> /* For ConvertSidToStringSidW. */
    4850
    4951#include <iprt/win/objbase.h>
     
    5557#include <iprt/assert.h>
    5658#include <iprt/alloca.h>
     59#include <iprt/dir.h>
     60#include <iprt/err.h>
     61#include <iprt/file.h>
    5762#include <iprt/mem.h>
    5863#include <iprt/path.h>   /* RTPATH_MAX, RTPATH_IS_SLASH */
    5964#include <iprt/string.h> /* RT_ZERO */
     65#include <iprt/stream.h>
     66#include <iprt/thread.h>
    6067#include <iprt/utf16.h>
    6168
     
    7986
    8087
     88/*********************************************************************************************************************************
     89*   Internal structures                                                                                                          *
     90*********************************************************************************************************************************/
     91/**
     92 * Structure for keeping a target's directory security context.
     93 */
     94typedef struct TGTDIRSECCTX
     95{
     96    /** Initialized status. */
     97    bool     fInitialized;
     98    /** Handle of the target's parent directory.
     99     *
     100     * Kept open while the context is around and initialized. */
     101    RTDIR    hParentDir;
     102    /** Absolute (resolved) path of the target directory. */
     103    char     szTargetDirAbs[RTPATH_MAX];
     104    /** Access mask which is forbidden for an ACE of type ACCESS_ALLOWED_ACE_TYPE. */
     105    uint32_t fAccessMaskForbidden;
     106    /** Array of well-known SIDs which are forbidden. */
     107    PSID    *paWellKnownSidsForbidden;
     108    /** Number of entries in \a paWellKnownSidsForbidden. */
     109    size_t   cWellKnownSidsForbidden;
     110} TGTDIRSECCTX;
     111/** Pointer to a target's directory security context. */
     112typedef TGTDIRSECCTX *PTGTDIRSECCTX;
     113
     114
     115/*********************************************************************************************************************************
     116*   Prototypes                                                                                                                   *
     117*********************************************************************************************************************************/
     118static void destroyTargetDirSecurityCtx(PTGTDIRSECCTX pCtx);
     119
     120
     121/*********************************************************************************************************************************
     122*   Globals                                                                                                                      *
     123*********************************************************************************************************************************/
     124static uint32_t     g_cRef = 0;
     125/** Our target directory security context.
     126 *
     127 * Has to be global in order to keep it around as long as the DLL is being loaded. */
     128static TGTDIRSECCTX g_TargetDirSecCtx = { 0 };
     129
    81130
    82131/**
     
    87136    RT_NOREF(hInst, uReason, pReserved);
    88137
     138#ifdef DEBUG
     139    WCHAR wszMsg[128];
     140    RTUtf16Printf(wszMsg, RT_ELEMENTS(wszMsg), "DllMain: hInst=%#x, uReason=%u (PID %u), g_cRef=%RU32\n",
     141                  hInst, uReason, GetCurrentProcessId(), g_cRef);
     142    OutputDebugStringW(wszMsg);
     143#endif
     144
     145    switch (uReason)
     146    {
     147        case DLL_PROCESS_ATTACH:
     148        {
     149            g_cRef++;
    89150#if 0
    90     /*
    91      * This is a trick for allowing the debugger to be attached, don't know if
    92      * there is an official way to do that, but this is a pretty efficient.
    93      *
    94      * Monitor the debug output in DbgView and be ready to start windbg when
    95      * the message below appear.  This will happen 3-4 times during install,
    96      * and 2-3 times during uninstall.
    97      *
    98      * Note! The DIFxApp.DLL will automatically trigger breakpoints when a
    99      *       debugger is attached.  Just continue on these.
    100      */
    101     if (uReason == DLL_PROCESS_ATTACH)
    102     {
    103         WCHAR wszMsg[128];
    104         RTUtf16Printf(wszMsg, RT_ELEMENTS(wszMsg), "Waiting for debugger to attach: windbg -g -G -p %u\n", GetCurrentProcessId());
    105         for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++)
    106         {
    107             OutputDebugStringW(wszMsg);
    108             Sleep(1001);
    109         }
    110         Sleep(1002);
    111         __debugbreak();
    112     }
     151            /*
     152             * This is a trick for allowing the debugger to be attached, don't know if
     153             * there is an official way to do that, but this is a pretty efficient.
     154             *
     155             * Monitor the debug output in DbgView and be ready to start windbg when
     156             * the message below appear.  This will happen 3-4 times during install,
     157             * and 2-3 times during uninstall.
     158             *
     159             * Note! The DIFxApp.DLL will automatically trigger breakpoints when a
     160             *       debugger is attached.  Just continue on these.
     161             */
     162            RTUtf16Printf(wszMsg, RT_ELEMENTS(wszMsg), "Waiting for debugger to attach: windbg -g -G -p %u\n", GetCurrentProcessId());
     163            for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++)
     164            {
     165                OutputDebugStringW(wszMsg);
     166                Sleep(1001);
     167            }
     168            Sleep(1002);
     169            __debugbreak();
    113170#endif
     171            break;
     172        }
     173
     174        case DLL_PROCESS_DETACH:
     175        {
     176            g_cRef--;
     177            break;
     178        }
     179
     180        default:
     181            break;
     182    }
    114183
    115184    return TRUE;
     
    117186
    118187/**
    119  * Format and add message to the MSI log.
     188 * Format a log message and print it to whatever is there (i.e. to the MSI log).
    120189 *
    121190 * UTF-16 strings are formatted using '%ls' (lowercase).
    122191 * ANSI strings are formatted using '%s' (uppercase).
     192 *
     193 * @returns VBox status code.
     194 * @param   hInstall            MSI installer handle. Optional and can be NULL.
     195 * @param   pszFmt              Format string.
     196 * @param   ...                 Variable arguments for format string.
    123197 */
    124 static UINT logStringF(MSIHANDLE hInstall, const char *pszFmt, ...)
    125 {
     198static int logStringF(MSIHANDLE hInstall, const char *pszFmt, ...)
     199{
     200    RTUTF16 wszVa[RTPATH_MAX + 256];
     201    va_list va;
     202    va_start(va, pszFmt);
     203    ssize_t cwc = RTUtf16PrintfV(wszVa, RT_ELEMENTS(wszVa), pszFmt, va);
     204    va_end(va);
     205
     206    RTUTF16 wszMsg[RTPATH_MAX + 256];
     207    cwc = RTUtf16Printf(wszMsg, sizeof(wszMsg), "VBoxInstallHelper: %ls", wszVa);
     208    if (cwc <= 0)
     209        return VERR_BUFFER_OVERFLOW;
     210
     211#ifdef DEBUG
     212    OutputDebugStringW(wszMsg);
     213#endif
     214#ifdef TESTCASE
     215    RTPrintf("%ls\n", wszMsg);
     216#endif
    126217    PMSIHANDLE hMSI = MsiCreateRecord(2 /* cParms */);
    127218    if (hMSI)
    128219    {
    129         wchar_t wszBuf[RTPATH_MAX + 256];
    130         va_list va;
    131         va_start(va, pszFmt);
    132         ssize_t cwc = RTUtf16PrintfV(wszBuf, RT_ELEMENTS(wszBuf), pszFmt, va);
    133         va_end(va);
    134 
    135         MsiRecordSetStringW(hMSI, 0, wszBuf);
     220        MsiRecordSetStringW(hMSI, 0, wszMsg);
    136221        MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO), hMSI);
    137 
    138222        MsiCloseHandle(hMSI);
    139         return cwc < RT_ELEMENTS(wszBuf) ? ERROR_SUCCESS : ERROR_BUFFER_OVERFLOW;
    140     }
    141     return ERROR_ACCESS_DENIED;
     223    }
     224
     225    return cwc < RT_ELEMENTS(wszVa) ? VINF_SUCCESS : VERR_BUFFER_OVERFLOW;
    142226}
    143227
     
    160244#endif
    161245    return ERROR_SUCCESS;
     246}
     247
     248/**
     249 * Initializes a target security context.
     250 *
     251 * @returns VBox status code.
     252 * @param   pCtx                Target directory security context to initialize.
     253 * @param   hModule             Windows installer module handle.
     254 * @param   pszPath             Target directory path to use.
     255 */
     256static int initTargetDirSecurityCtx(PTGTDIRSECCTX pCtx, MSIHANDLE hModule, const char *pszPath)
     257{
     258    if (pCtx->fInitialized)
     259        return VINF_SUCCESS;
     260
     261#ifdef DEBUG
     262    logStringF(hModule, "initTargetDirSecurityCtx: pszPath=%s\n", pszPath);
     263#endif
     264
     265    char szPathTemp[RTPATH_MAX];
     266    int vrc = RTStrCopy(szPathTemp, sizeof(szPathTemp), pszPath);
     267    if (RT_FAILURE(vrc))
     268        return vrc;
     269
     270    /* Try to find a parent path which exists. */
     271    char szPathParentAbs[RTPATH_MAX] = { 0 };
     272    for (int i = 0; i < 256; i++) /* Failsafe counter. */
     273    {
     274        RTPathStripTrailingSlash(szPathTemp);
     275        RTPathStripFilename(szPathTemp);
     276        vrc = RTPathReal(szPathTemp, szPathParentAbs, sizeof(szPathParentAbs));
     277        if (RT_SUCCESS(vrc))
     278            break;
     279    }
     280
     281    if (RT_FAILURE(vrc))
     282    {
     283        logStringF(hModule, "initTargetDirSecurityCtx: No existing / valid parent directory found (%Rrc), giving up\n", vrc);
     284        return vrc;
     285    }
     286
     287    RTDIR hParentDir;
     288    vrc = RTDirOpen(&hParentDir, szPathParentAbs);
     289    if (RT_FAILURE(vrc))
     290    {
     291        logStringF(hModule, "initTargetDirSecurityCtx: Locking parent directory '%s' failed with %Rrc\n", szPathParentAbs, vrc);
     292        return vrc;
     293    }
     294
     295#ifdef DEBUG
     296    logStringF(hModule, "initTargetDirSecurityCtx: Locked parent directory '%s'\n", szPathParentAbs);
     297#endif
     298
     299    char szPathTargetAbs[RTPATH_MAX];
     300    vrc = RTPathReal(pszPath, szPathTargetAbs, sizeof(szPathTargetAbs));
     301    if (RT_FAILURE(vrc))
     302        vrc = RTStrCopy(szPathTargetAbs, sizeof(szPathTargetAbs), pszPath);
     303    if (RT_FAILURE(vrc))
     304    {
     305        logStringF(hModule, "initTargetDirSecurityCtx: Failed to resolve absolute target path (%Rrc)\n", vrc);
     306        return vrc;
     307    }
     308
     309#ifdef DEBUG
     310    logStringF(hModule, "initTargetDirSecurityCtx: szPathTargetAbs=%s, szPathParentAbs=%s\n", szPathTargetAbs, szPathParentAbs);
     311#endif
     312
     313    /* Target directory validation. */
     314    if (   !RTStrCmp(szPathTargetAbs, szPathParentAbs) /* Don't allow installation into root directories. */
     315        ||  RTStrStr(szPathTargetAbs, ".."))
     316    {
     317        logStringF(hModule, "initTargetDirSecurityCtx: Directory '%s' invalid", szPathTargetAbs);
     318        vrc = VERR_INVALID_NAME;
     319    }
     320
     321    if (RT_SUCCESS(vrc))
     322    {
     323        RTFSOBJINFO fsObjInfo;
     324        vrc = RTPathQueryInfo(szPathParentAbs, &fsObjInfo, RTFSOBJATTRADD_NOTHING);
     325        if (RT_SUCCESS(vrc))
     326        {
     327            if (RTFS_IS_DIRECTORY(fsObjInfo.Attr.fMode)) /* No symlinks or other fun stuff. */
     328            {
     329                static WELL_KNOWN_SID_TYPE aForbiddenWellKnownSids[] =
     330                {
     331                    WinNullSid,
     332                    WinWorldSid,
     333                    WinAuthenticatedUserSid,
     334                    WinBuiltinUsersSid,
     335                    WinBuiltinGuestsSid,
     336                    WinBuiltinPowerUsersSid
     337                };
     338
     339                pCtx->paWellKnownSidsForbidden = (PSID *)RTMemAlloc(sizeof(PSID) * RT_ELEMENTS(aForbiddenWellKnownSids));
     340                AssertPtrReturn(pCtx->paWellKnownSidsForbidden, VERR_NO_MEMORY);
     341
     342                size_t i = 0;
     343                for(; i < RT_ELEMENTS(aForbiddenWellKnownSids); i++)
     344                {
     345                    pCtx->paWellKnownSidsForbidden[i] = RTMemAlloc(SECURITY_MAX_SID_SIZE);
     346                    AssertPtrBreakStmt(pCtx->paWellKnownSidsForbidden, vrc = VERR_NO_MEMORY);
     347                    DWORD cbSid = SECURITY_MAX_SID_SIZE;
     348                    if (!CreateWellKnownSid(aForbiddenWellKnownSids[i], NULL, pCtx->paWellKnownSidsForbidden[i], &cbSid))
     349                    {
     350                        vrc = RTErrConvertFromWin32(GetLastError());
     351                        logStringF(hModule, "initTargetDirSecurityCtx: Creating SID (index %zu) failed with %Rrc\n", i, vrc);
     352                        break;
     353                    }
     354                }
     355
     356                if (RT_SUCCESS(vrc))
     357                {
     358                    vrc = RTStrCopy(pCtx->szTargetDirAbs, sizeof(pCtx->szTargetDirAbs), szPathTargetAbs);
     359                    if (RT_SUCCESS(vrc))
     360                    {
     361                        pCtx->fInitialized            = true;
     362                        pCtx->hParentDir              = hParentDir;
     363                        pCtx->cWellKnownSidsForbidden = i;
     364                        pCtx->fAccessMaskForbidden    = FILE_WRITE_DATA
     365                                                      | FILE_APPEND_DATA
     366                                                      | FILE_WRITE_ATTRIBUTES
     367                                                      | FILE_WRITE_EA;
     368
     369                        RTFILE fh;
     370                        RTFileOpen(&fh, "c:\\temp\\targetdir.ctx", RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE | RTFILE_O_WRITE);
     371                        RTFileClose(fh);
     372
     373                        return VINF_SUCCESS;
     374                    }
     375                }
     376            }
     377            else
     378                vrc = VERR_INVALID_NAME;
     379        }
     380    }
     381
     382    RTDirClose(hParentDir);
     383
     384    while (pCtx->cWellKnownSidsForbidden--)
     385    {
     386        RTMemFree(pCtx->paWellKnownSidsForbidden[pCtx->cWellKnownSidsForbidden]);
     387        pCtx->paWellKnownSidsForbidden[pCtx->cWellKnownSidsForbidden] = NULL;
     388    }
     389
     390    logStringF(hModule, "initTargetDirSecurityCtx: Initialization failed failed with %Rrc\n", vrc);
     391    return vrc;
     392}
     393
     394/**
     395 * Destroys a target security context.
     396 *
     397 * @returns VBox status code.
     398 * @param   pCtx                Target directory security context to destroy.
     399 */
     400static void destroyTargetDirSecurityCtx(PTGTDIRSECCTX pCtx)
     401{
     402    if (   !pCtx
     403        || !pCtx->fInitialized)
     404        return;
     405
     406    if (pCtx->hParentDir != NIL_RTDIR)
     407    {
     408        RTDirClose(pCtx->hParentDir);
     409        pCtx->hParentDir = NIL_RTDIR;
     410    }
     411    RT_ZERO(pCtx->szTargetDirAbs);
     412
     413    for (size_t i = 0; i < pCtx->cWellKnownSidsForbidden; i++)
     414        RTMemFree(pCtx->paWellKnownSidsForbidden[i]);
     415    pCtx->cWellKnownSidsForbidden = 0;
     416
     417    RTMemFree(pCtx->paWellKnownSidsForbidden);
     418    pCtx->paWellKnownSidsForbidden = NULL;
     419
     420    RTFileDelete("c:\\temp\\targetdir.ctx");
     421
     422    logStringF(NULL, "destroyTargetDirSecurityCtx\n");
     423}
     424
     425#ifdef DEBUG
     426/**
     427 * Returns a stingified version of an ACE type.
     428 *
     429 * @returns Stingified version of an ACE type.
     430 * @param   uType               ACE type.
     431 */
     432inline const char *dbgAceTypeToString(uint8_t uType)
     433{
     434    switch (uType)
     435    {
     436        RT_CASE_RET_STR(ACCESS_ALLOWED_ACE_TYPE);
     437        RT_CASE_RET_STR(ACCESS_DENIED_ACE_TYPE);
     438        RT_CASE_RET_STR(SYSTEM_AUDIT_ACE_TYPE);
     439        RT_CASE_RET_STR(SYSTEM_ALARM_ACE_TYPE);
     440        default: break;
     441    }
     442
     443    return "<Invalid>";
     444}
     445
     446/**
     447 * Returns an allocated string for a SID containing the user/domain name.
     448 *
     449 * @returns Allocated string (UTF-8). Must be free'd using RTStrFree().
     450 * @param   pSid                SID to return allocated string for.
     451 */
     452inline char *dbgSidToNameA(const PSID pSid)
     453{
     454    char *pszName = NULL;
     455    int   vrc     = VINF_SUCCESS;
     456
     457    LPWSTR pwszSid = NULL;
     458    if (ConvertSidToStringSid(pSid, &pwszSid))
     459    {
     460        SID_NAME_USE SidNameUse;
     461
     462        WCHAR wszUser[MAX_PATH];
     463        DWORD cbUser   = sizeof(wszUser);
     464        WCHAR wszDomain[MAX_PATH];
     465        DWORD cbDomain = sizeof(wszDomain);
     466        if (LookupAccountSid(NULL, pSid, wszUser, &cbUser, wszDomain, &cbDomain, &SidNameUse))
     467        {
     468            RTUTF16 wszName[RTPATH_MAX];
     469            if (RTUtf16Printf(wszName, RT_ELEMENTS(wszName), "%ls%s%ls (%ls)",
     470                              wszUser, wszDomain[0] == L'\0' ? "" : "\\", wszDomain, pwszSid))
     471            {
     472                vrc = RTUtf16ToUtf8(wszName, &pszName);
     473            }
     474            else
     475                vrc = VERR_NO_MEMORY;
     476        }
     477        else
     478            vrc = RTStrAPrintf(&pszName, "<Lookup Error>");
     479
     480        LocalFree(pwszSid);
     481    }
     482    else
     483        vrc = VERR_NOT_FOUND;
     484
     485    return RT_SUCCESS(vrc) ? pszName : "<Invalid>";
     486}
     487#endif /* DEBUG */
     488
     489/**
     490 * Checks a single target path whether it's safe to use or not.
     491 *
     492 * We check if the given path is owned by "NT Service\TrustedInstaller" and therefore assume that it's safe to use.
     493 *
     494 * @returns VBox status code. On error the path should be considered unsafe.
     495 * @retval  VERR_INVALID_NAME if the given path is considered unsafe.
     496 * @retval  VINF_SUCCESS if the given path is found to be safe to use.
     497 * @param   hModule             Windows installer module handle.
     498 * @param   pszPath             Path to check.
     499 */
     500static int checkTargetDirOne(MSIHANDLE hModule, PTGTDIRSECCTX pCtx, const char *pszPath)
     501{
     502    logStringF(hModule, "checkTargetDirOne: Checking '%s' ...", pszPath);
     503
     504    PRTUTF16 pwszPath;
     505    int vrc = RTStrToUtf16(pszPath, &pwszPath);
     506    if (RT_FAILURE(vrc))
     507        return vrc;
     508
     509    PACL      pDacl = NULL;
     510    PSECURITY_DESCRIPTOR pSecurityDescriptor = { 0 };
     511    DWORD dwErr = GetNamedSecurityInfo(pwszPath, SE_FILE_OBJECT, GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
     512                                       NULL, NULL, NULL, NULL, &pSecurityDescriptor);
     513    if (dwErr == ERROR_SUCCESS)
     514    {
     515        BOOL fDaclPresent          = FALSE;
     516        BOOL fDaclDefaultedIgnored = FALSE;
     517        if (GetSecurityDescriptorDacl(pSecurityDescriptor, &fDaclPresent,
     518                                      &pDacl, &fDaclDefaultedIgnored))
     519        {
     520            if (   !fDaclPresent
     521                || !pDacl)
     522            {
     523                /* Bail out early if the DACL isn't provided or is missing. */
     524                vrc = VERR_INVALID_NAME;
     525            }
     526            else
     527            {
     528                ACL_SIZE_INFORMATION aclSizeInfo;
     529                RT_ZERO(aclSizeInfo);
     530                if (GetAclInformation(pDacl, &aclSizeInfo, sizeof(aclSizeInfo), AclSizeInformation))
     531                {
     532                    for(DWORD idxACE = 0; idxACE < aclSizeInfo.AceCount; idxACE++)
     533                    {
     534                        ACE_HEADER *pAceHdr = NULL;
     535                        if (GetAce(pDacl, idxACE, (LPVOID *)&pAceHdr))
     536                        {
     537#ifdef DEBUG
     538                            logStringF(hModule, "checkTargetDirOne: ACE type=%s, flags=%#x, size=%#x",
     539                                       dbgAceTypeToString(pAceHdr->AceType), pAceHdr->AceFlags, pAceHdr->AceSize);
     540#endif
     541                            /* Note: We print the ACEs in canonoical order. */
     542                            switch (pAceHdr->AceType)
     543                            {
     544                                case ACCESS_ALLOWED_ACE_TYPE: /* We're only interested in the ALLOW ACE. */
     545                                {
     546                                    ACCESS_ALLOWED_ACE const *pAce = (ACCESS_ALLOWED_ACE *)pAceHdr;
     547                                    PSID const pSid                = (PSID)&pAce->SidStart;
     548#ifdef DEBUG
     549                                    char *pszSid = dbgSidToNameA(pSid);
     550                                    logStringF(hModule, "checkTargetDirOne:\t%s fMask=%#x", pszSid, pAce->Mask);
     551                                    RTStrFree(pszSid);
     552#endif
     553                                    /* We check the flags here first for performance reasons. */
     554                                    if ((pAce->Mask & pCtx->fAccessMaskForbidden) == pCtx->fAccessMaskForbidden)
     555                                    {
     556                                        for (size_t idxSID = 0; idxSID < pCtx->cWellKnownSidsForbidden; idxSID++)
     557                                        {
     558                                            PSID const pSidForbidden = pCtx->paWellKnownSidsForbidden[idxSID];
     559                                            bool const fForbidden    = EqualSid(pSid, pSidForbidden);
     560#ifdef DEBUG
     561                                            char *pszName = dbgSidToNameA(pSidForbidden);
     562                                            logStringF(hModule, "checkTargetDirOne:\t%s : %s",
     563                                                       fForbidden ? "** FORBIDDEN **" : "ALLOWED        ", pszName);
     564                                            RTStrFree(pszName);
     565#endif /* DEBUG */
     566                                            if (fForbidden)
     567                                            {
     568                                                vrc = VERR_INVALID_NAME;
     569                                                break;
     570                                            }
     571                                        }
     572                                    }
     573
     574                                    break;
     575                                }
     576
     577                                case ACCESS_DENIED_ACE_TYPE: /* We're only interested in the ALLOW ACE. */
     578                                {
     579                                    ACCESS_DENIED_ACE const *pAce = (ACCESS_DENIED_ACE *)pAceHdr;
     580#ifdef DEBUG
     581                                    LPWSTR pwszSid = NULL;
     582                                    ConvertSidToStringSid((PSID)&pAce->SidStart, &pwszSid);
     583
     584                                    logStringF(hModule, "checkTargetDirOne:\t%ls fMask=%#x (generic %#x specific %#x)",
     585                                               pwszSid ? pwszSid : L"<Allocation Error>", pAce->Mask);
     586#endif /* DEBUG */
     587                                    LocalFree(pwszSid);
     588
     589                                    /* Ignore everything else. */
     590                                    break;
     591                                }
     592                            }
     593                        }
     594                        else
     595                            dwErr = GetLastError();
     596
     597                        /* No point in checking further if we failed somewhere above. */
     598                        if (RT_FAILURE(vrc))
     599                            break;
     600
     601                    } /* for ACE */
     602                }
     603                else
     604                    dwErr = GetLastError();
     605            }
     606        }
     607        else
     608            dwErr = GetLastError();
     609
     610        LocalFree(pSecurityDescriptor);
     611    }
     612    else
     613        dwErr = GetLastError();
     614
     615    if (RT_SUCCESS(vrc))
     616        vrc = RTErrConvertFromWin32(dwErr);
     617
     618#ifdef DEBUG
     619    logStringF(hModule, "checkTargetDirOne: Returning %Rrc", vrc);
     620#endif
     621
     622    if (   RT_FAILURE(vrc)
     623        && vrc != VERR_INVALID_NAME)
     624        logStringF(hModule, "checkTargetDirOne: Failed with %Rrc (%#x)", vrc, dwErr);
     625
     626    return vrc;
     627}
     628
     629/**
     630 * Checks whether the path in the public property INSTALLDIR has the correct ACL permissions and returns whether
     631 * it's valid or not.
     632 *
     633 * Called from the MSI installer as a custom action.
     634 *
     635 * @returns Success status (acccording to MSI custom actions).
     636 * @retval  ERROR_SUCCESS if checking the target directory turned out to be valid.
     637 * @retval  ERROR_NO_NET_OR_BAD_PATH is the target directory is invalid.
     638 * @param   hModule             Windows installer module handle.
     639 *
     640 * @note    Sets private property VBox_Target_Dir_Is_Valid to "1" (true) if the given target path is valid,
     641 *          or "0" (false) if it is not. An empty target directory is considered to be valid (i.e. INSTALLDIR not set yet).
     642 *
     643 * @sa      @bugref{10616}
     644 */
     645UINT __stdcall CheckTargetDir(MSIHANDLE hModule)
     646{
     647    char *pszTargetDir;
     648
     649    int vrc = VBoxGetMsiPropUtf8(hModule, "INSTALLDIR", &pszTargetDir);
     650    if (RT_SUCCESS(vrc))
     651    {
     652        logStringF(hModule, "CheckTargetDir: Checking target directory '%s' ...", pszTargetDir);
     653
     654        if (!RTStrNLen(pszTargetDir, RTPATH_MAX))
     655        {
     656            logStringF(hModule, "CheckTargetDir: No INSTALLDIR set (yet), skipping ...");
     657            VBoxSetMsiProp(hModule, L"VBox_Target_Dir_Is_Valid", L"1");
     658        }
     659        else
     660        {
     661            union
     662            {
     663                RTPATHPARSED    Parsed;
     664                uint8_t         ab[RTPATH_MAX];
     665            } u;
     666
     667            vrc = RTPathParse(pszTargetDir, &u.Parsed, sizeof(u), RTPATH_STR_F_STYLE_DOS);
     668            if (RT_SUCCESS(vrc))
     669            {
     670                if (u.Parsed.fProps & RTPATH_PROP_DOTDOT_REFS)
     671                    vrc = VERR_INVALID_PARAMETER;
     672                if (RT_SUCCESS(vrc))
     673                {
     674                    vrc = initTargetDirSecurityCtx(&g_TargetDirSecCtx, hModule, pszTargetDir);
     675                    if (RT_SUCCESS(vrc))
     676                    {
     677                        uint16_t idxComp = u.Parsed.cComps;
     678                        char     szPathToCheck[RTPATH_MAX];
     679                        while (idxComp > 1) /* We traverse backwards from INSTALLDIR and leave out the root (e.g. C:\"). */
     680                        {
     681                            u.Parsed.cComps = idxComp;
     682                            vrc = RTPathParsedReassemble(pszTargetDir, &u.Parsed, RTPATH_STR_F_STYLE_DOS,
     683                                                         szPathToCheck, sizeof(szPathToCheck));
     684                            if (RT_FAILURE(vrc))
     685                                break;
     686                            if (RTDirExists(szPathToCheck))
     687                            {
     688                                vrc = checkTargetDirOne(hModule, &g_TargetDirSecCtx, szPathToCheck);
     689                                if (RT_FAILURE(vrc))
     690                                    break;
     691                            }
     692                            else
     693                                logStringF(hModule, "CheckTargetDir: Path '%s' does not exist (yet)", szPathToCheck);
     694                            idxComp--;
     695                        }
     696
     697                        destroyTargetDirSecurityCtx(&g_TargetDirSecCtx);
     698                    }
     699                    else
     700                        logStringF(hModule, "CheckTargetDir: initTargetDirSecurityCtx failed with %Rrc\n", vrc);
     701
     702                    if (RT_SUCCESS(vrc))
     703                        VBoxSetMsiProp(hModule, L"VBox_Target_Dir_Is_Valid", L"1");
     704                }
     705            }
     706            else
     707                logStringF(hModule, "CheckTargetDir: Parsing path failed with %Rrc", vrc);
     708        }
     709
     710        RTStrFree(pszTargetDir);
     711    }
     712
     713    if (RT_FAILURE(vrc)) /* On failure (or when in doubt), mark the installation directory as invalid. */
     714    {
     715        logStringF(hModule, "CheckTargetDir: Checking failed with %Rrc", vrc);
     716        VBoxSetMsiProp(hModule, L"VBox_Target_Dir_Is_Valid", L"0");
     717    }
     718
     719    /* Return back outcome to the MSI engine. */
     720    return RT_SUCCESS(vrc) ? ERROR_SUCCESS : ERROR_NO_NET_OR_BAD_PATH;
    162721}
    163722
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