VirtualBox

Changeset 39867 in vbox for trunk


Ignore:
Timestamp:
Jan 25, 2012 2:44:50 AM (13 years ago)
Author:
vboxsync
Message:

process-win.cpp: Splitting monster function.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/r3/win/process-win.cpp

    r39866 r39867  
    699699    /* If we don't have the Userenv-API for whatever reason or something with the
    700700     * native environment block failed, try to return at least our own environment block. */
     701    /** @todo this probably isn't a great idea if CreateEnvironmentBlock fails. */
    701702    if (RT_FAILURE(rc))
    702703        rc = RTEnvQueryUtf16Block(hEnv, ppwszBlock);
     
    745746
    746747
     748/**
     749 * Method \#2.
     750 */
     751static int rtProcWinCreateAsUser2(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine,
     752                                  RTENV hEnv, DWORD dwCreationFlags,
     753                                  STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, uint32_t fFlags)
     754{
     755    /*
     756     * So if we want to start a process from a service (RTPROC_FLAGS_SERVICE),
     757     * we have to do the following:
     758     * - Check the credentials supplied and get the user SID.
     759     * - If valid get the correct Explorer/VBoxTray instance corresponding to that
     760     *   user. This of course is only possible if that user is logged in (over
     761     *   physical console or terminal services).
     762     * - If we found the user's Explorer/VBoxTray app, use and modify the token to
     763     *   use it in order to allow the newly started process to access the user's
     764     *   desktop. If there's no Explorer/VBoxTray app we cannot display the started
     765     *   process (but run it without UI).
     766     *
     767     * The following restrictions apply:
     768     * - A process only can show its UI when the user the process should run
     769     *   under is logged in (has a desktop).
     770     * - We do not want to display a process of user A run on the desktop
     771     *   of user B on multi session systems.
     772     *
     773     * The following rights are needed in order to use LogonUserW and
     774     * CreateProcessAsUserW, so the local policy has to be modified to:
     775     *  - SE_TCB_NAME = Act as part of the operating system
     776     *  - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a token object
     777     *  - SE_INCREASE_QUOTA_NAME
     778     *
     779     * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
     780     */
     781    DWORD dwErr = NO_ERROR;
     782    PHANDLE phToken = NULL;
     783    HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
     784    int rc = rtProcWinUserLogon(pwszUser, pwszPassword, NULL /* Domain */, &hTokenLogon);
     785    if (RT_SUCCESS(rc))
     786    {
     787        DWORD fRc;
     788        bool fFound = false;
     789        HANDLE hTokenUserDesktop = INVALID_HANDLE_VALUE;
     790
     791        if (fFlags & RTPROC_FLAGS_SERVICE)
     792        {
     793            DWORD cbSid = 0; /* Must be zero to query size! */
     794            DWORD cchDomain = 0;
     795            SID_NAME_USE sidNameUse = SidTypeUser;
     796            fRc = LookupAccountNameW(NULL,
     797                                     pwszUser,
     798                                     NULL,
     799                                     &cbSid,
     800                                     NULL,
     801                                     &cchDomain,
     802                                     &sidNameUse);
     803            if (!fRc)
     804                dwErr = GetLastError();
     805            if (   !fRc
     806                && dwErr == ERROR_INSUFFICIENT_BUFFER
     807                && cbSid > 0)
     808            {
     809                dwErr = NO_ERROR;
     810
     811                PSID pSid = (PSID)RTMemAlloc(cbSid * sizeof(wchar_t)); /** @todo r=bird: What's the relationship between wchar_t and PSID? */
     812                AssertPtrReturn(pSid, VERR_NO_MEMORY); /** @todo r=bird: Leaking token handles when we're out of memory...  */
     813
     814                PRTUTF16 pwszDomain = NULL;
     815                if (cchDomain > 0)
     816                {
     817                    pwszDomain = (PRTUTF16)RTMemAlloc(cchDomain * sizeof(RTUTF16));
     818                    AssertPtrReturn(pwszDomain, VERR_NO_MEMORY); /** @todo r=bird: Leaking token handles when we're out of memory...  */
     819                }
     820
     821                /* Note: Also supports FQDNs! */
     822                if (   LookupAccountNameW(NULL,            /* lpSystemName */
     823                                          pwszUser,
     824                                          pSid,
     825                                          &cbSid,
     826                                          pwszDomain,
     827                                          &cchDomain,
     828                                          &sidNameUse)
     829                    && IsValidSid(pSid))
     830                {
     831                    /* Array of process names we want to look for. */
     832                    static const char * const s_papszProcNames[] =
     833                    {
     834#ifdef VBOX                 /* The explorer entry is a fallback in case GA aren't installed. */
     835                        { "VBoxTray.exe" },
     836#endif
     837                        { "explorer.exe" },
     838                        NULL
     839                    };
     840                    fFound = rtProcWinFindTokenByProcess(s_papszProcNames, pSid, &hTokenUserDesktop);
     841                }
     842                else
     843                    dwErr = GetLastError(); /* LookupAccountNameW() failed. */
     844                RTMemFree(pSid);
     845                RTMemFree(pwszDomain);
     846            }
     847        }
     848        else /* !RTPROC_FLAGS_SERVICE */
     849        {
     850            /* Nothing to do here right now. */
     851        }
     852
     853        /** @todo Hmm, this function already is too big! We need to split
     854         *        it up into several small parts. */
     855
     856        /* If we got an error due to account lookup/loading above, don't
     857         * continue here. */
     858        if (dwErr == NO_ERROR)
     859        {
     860            /*
     861             * If we didn't find a matching VBoxTray, just use the token we got
     862             * above from LogonUserW(). This enables us to at least run processes with
     863             * desktop interaction without UI.
     864             */
     865            phToken = fFound ? &hTokenUserDesktop : &hTokenLogon;
     866            RTLDRMOD hUserenv;
     867            int rc = RTLdrLoad("Userenv.dll", &hUserenv);
     868            if (RT_SUCCESS(rc))
     869            {
     870                PFNLOADUSERPROFILEW pfnLoadUserProfileW;
     871                rc = RTLdrGetSymbol(hUserenv, "LoadUserProfileW", (void**)&pfnLoadUserProfileW);
     872                if (RT_SUCCESS(rc))
     873                {
     874                    PFNUNLOADUSERPROFILE pfnUnloadUserProfile;
     875                    rc = RTLdrGetSymbol(hUserenv, "UnloadUserProfile", (void**)&pfnUnloadUserProfile);
     876                    if (RT_SUCCESS(rc))
     877                    {
     878                        PROFILEINFOW profileInfo;
     879                        if (!(fFlags & RTPROC_FLAGS_NO_PROFILE))
     880                        {
     881                            RT_ZERO(profileInfo);
     882                            profileInfo.dwSize = sizeof(profileInfo);
     883                            profileInfo.lpUserName = pwszUser;
     884                            profileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */
     885
     886                            if (!pfnLoadUserProfileW(*phToken, &profileInfo))
     887                                dwErr = GetLastError();
     888                        }
     889
     890                        if (dwErr == NO_ERROR)
     891                        {
     892                            PRTUTF16 pwszzBlock;
     893                            rc = rtProcWinCreateEnvFromToken(*phToken, hEnv, &pwszzBlock);
     894                            if (RT_SUCCESS(rc))
     895                            {
     896                                /*
     897                                 * Useful KB articles:
     898                                 *      http://support.microsoft.com/kb/165194/
     899                                 *      http://support.microsoft.com/kb/184802/
     900                                 *      http://support.microsoft.com/kb/327618/
     901                                 */
     902                                fRc = CreateProcessAsUserW(*phToken,
     903                                                           pwszExec,
     904                                                           pwszCmdLine,
     905                                                           NULL,         /* pProcessAttributes */
     906                                                           NULL,         /* pThreadAttributes */
     907                                                           TRUE,         /* fInheritHandles */
     908                                                           dwCreationFlags,
     909                                                           pwszzBlock,
     910                                                           NULL,         /* pCurrentDirectory */
     911                                                           pStartupInfo,
     912                                                           pProcInfo);
     913                                if (fRc)
     914                                    dwErr = NO_ERROR;
     915                                else
     916                                    dwErr = GetLastError(); /* CreateProcessAsUserW() failed. */
     917                                rtProcWinDestoryEnv(pwszzBlock);
     918                            }
     919
     920                            if (!(fFlags & RTPROC_FLAGS_NO_PROFILE))
     921                            {
     922                                fRc = pfnUnloadUserProfile(*phToken, profileInfo.hProfile);
     923#ifdef RT_STRICT
     924                                if (!fRc)
     925                                {
     926                                    DWORD dwErr2 = GetLastError();
     927                                    AssertMsgFailed(("Unloading user profile failed with error %u (%#x) - Are all handles closed? (dwErr=%u)",
     928                                                     dwErr2, dwErr2, dwErr));
     929                                }
     930#endif
     931                            }
     932                        }
     933                    }
     934                }
     935                RTLdrClose(hUserenv);
     936            } /* Userenv.dll found/loaded? */
     937        } /* Account lookup succeeded? */
     938        if (hTokenUserDesktop != INVALID_HANDLE_VALUE)
     939            CloseHandle(hTokenUserDesktop);
     940        rtProcWinUserLogoff(hTokenLogon);
     941    }
     942
     943    if (   RT_SUCCESS(rc)
     944        && dwErr != NO_ERROR)
     945        rc = rtProcWinMapErrorCodes(dwErr);
     946    return rc;
     947}
     948
     949
     950/**
     951 * Method \#1.
     952 *
     953 * This may fail on too old (NT4) platforms or if the calling process
     954 * is running on a SYSTEM account (like a service, ERROR_ACCESS_DENIED) on newer
     955 * platforms (however, this works on W2K!).
     956 */
     957static int rtProcWinCreateAsUser1(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine,
     958                                  RTENV hEnv, DWORD dwCreationFlags,
     959                                  STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, uint32_t fFlags)
     960{
     961    RTLDRMOD hAdvAPI32;
     962    int rc = RTLdrLoad("Advapi32.dll", &hAdvAPI32);
     963    if (RT_SUCCESS(rc))
     964    {
     965        PFNCREATEPROCESSWITHLOGON pfnCreateProcessWithLogonW;
     966        rc = RTLdrGetSymbol(hAdvAPI32, "CreateProcessWithLogonW", (void **)&pfnCreateProcessWithLogonW);
     967        if (RT_SUCCESS(rc))
     968        {
     969            PRTUTF16 pwszzBlock;
     970            rc = rtProcWinCreateEnvFromAccount(pwszUser, pwszPassword, NULL /* Domain */,
     971                                               hEnv, &pwszzBlock);
     972            if (RT_SUCCESS(rc))
     973            {
     974                BOOL fRc = pfnCreateProcessWithLogonW(pwszUser,
     975                                                      NULL,                       /* lpDomain*/
     976                                                      pwszPassword,
     977                                                      1 /*LOGON_WITH_PROFILE*/,   /* dwLogonFlags */
     978                                                      pwszExec,
     979                                                      pwszCmdLine,
     980                                                      dwCreationFlags,
     981                                                      pwszzBlock,
     982                                                      NULL,                       /* pCurrentDirectory */
     983                                                      pStartupInfo,
     984                                                      pProcInfo);
     985                if (!fRc)
     986                    rc = rtProcWinMapErrorCodes(GetLastError());
     987                rtProcWinDestoryEnv(pwszzBlock);
     988            }
     989        }
     990        RTLdrClose(hAdvAPI32);
     991    }
     992    return rc;
     993}
     994
     995
    747996static int rtProcWinCreateAsUser(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine,
    748997                                 RTENV hEnv, DWORD dwCreationFlags,
    749998                                 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, uint32_t fFlags)
    750999{
    751     int rc = VINF_SUCCESS;
    752     BOOL fRc = FALSE;
    753     DWORD dwErr = NO_ERROR;
    754 
    7551000    /*
    7561001     * If we run as a service CreateProcessWithLogon will fail,
    7571002     * so don't even try it (because of Local System context).
    7581003     */
     1004    int rc = VERR_TRY_AGAIN;
    7591005    if (!(fFlags & RTPROC_FLAGS_SERVICE))
    760     {
    761         RTLDRMOD hAdvAPI32;
    762         rc = RTLdrLoad("Advapi32.dll", &hAdvAPI32);
    763         if (RT_SUCCESS(rc))
    764         {
    765             /*
    766              * This may fail on too old (NT4) platforms or if the calling process
    767              * is running on a SYSTEM account (like a service, ERROR_ACCESS_DENIED) on newer
    768              * platforms (however, this works on W2K!).
    769              */
    770             PFNCREATEPROCESSWITHLOGON pfnCreateProcessWithLogonW;
    771             rc = RTLdrGetSymbol(hAdvAPI32, "CreateProcessWithLogonW", (void **)&pfnCreateProcessWithLogonW);
    772             if (RT_SUCCESS(rc))
    773             {
    774                 PRTUTF16 pwszzBlock;
    775                 rc = rtProcWinCreateEnvFromAccount(pwszUser, pwszPassword, NULL /* Domain */,
    776                                                    hEnv, &pwszzBlock);
    777                 if (RT_SUCCESS(rc))
    778                 {
    779                     fRc = pfnCreateProcessWithLogonW(pwszUser,
    780                                                      NULL,                       /* lpDomain*/
    781                                                      pwszPassword,
    782                                                      1 /*LOGON_WITH_PROFILE*/,   /* dwLogonFlags */
    783                                                      pwszExec,
    784                                                      pwszCmdLine,
    785                                                      dwCreationFlags,
    786                                                      pwszzBlock,
    787                                                      NULL,                       /* pCurrentDirectory */
    788                                                      pStartupInfo,
    789                                                      pProcInfo);
    790                     if (!fRc)
    791                         rc = rtProcWinMapErrorCodes(GetLastError());
    792                     rtProcWinDestoryEnv(pwszzBlock);
    793                 }
    794             }
    795             RTLdrClose(hAdvAPI32);
    796         }
    797     }
     1006        rc = rtProcWinCreateAsUser1(pwszUser, pwszPassword, pwszExec, pwszCmdLine, hEnv, dwCreationFlags, pStartupInfo, pProcInfo, fFlags);
    7981007
    7991008    /*
     
    8011010     * we're running as a Windows service?
    8021011     */
    803     if (   RT_FAILURE(rc)
    804         || (fFlags & RTPROC_FLAGS_SERVICE))
    805     {
    806         /*
    807          * So if we want to start a process from a service (RTPROC_FLAGS_SERVICE),
    808          * we have to do the following:
    809          * - Check the credentials supplied and get the user SID.
    810          * - If valid get the correct Explorer/VBoxTray instance corresponding to that
    811          *   user. This of course is only possible if that user is logged in (over
    812          *   physical console or terminal services).
    813          * - If we found the user's Explorer/VBoxTray app, use and modify the token to
    814          *   use it in order to allow the newly started process to access the user's
    815          *   desktop. If there's no Explorer/VBoxTray app we cannot display the started
    816          *   process (but run it without UI).
    817          *
    818          * The following restrictions apply:
    819          * - A process only can show its UI when the user the process should run
    820          *   under is logged in (has a desktop).
    821          * - We do not want to display a process of user A run on the desktop
    822          *   of user B on multi session systems.
    823          *
    824          * The following rights are needed in order to use LogonUserW and
    825          * CreateProcessAsUserW, so the local policy has to be modified to:
    826          *  - SE_TCB_NAME = Act as part of the operating system
    827          *  - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a token object
    828          *  - SE_INCREASE_QUOTA_NAME
    829          *
    830          * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
    831          */
    832         PHANDLE phToken = NULL;
    833         HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
    834         rc = rtProcWinUserLogon(pwszUser, pwszPassword, NULL /* Domain */, &hTokenLogon);
    835         if (RT_SUCCESS(rc))
    836         {
    837             bool fFound = false;
    838             HANDLE hTokenUserDesktop = INVALID_HANDLE_VALUE;
    839 
    840             if (fFlags & RTPROC_FLAGS_SERVICE)
    841             {
    842                 DWORD cbSid = 0; /* Must be zero to query size! */
    843                 DWORD cchDomain = 0;
    844                 SID_NAME_USE sidNameUse = SidTypeUser;
    845                 fRc = LookupAccountNameW(NULL,
    846                                          pwszUser,
    847                                          NULL,
    848                                          &cbSid,
    849                                          NULL,
    850                                          &cchDomain,
    851                                          &sidNameUse);
    852                 if (!fRc)
    853                     dwErr = GetLastError();
    854                 if (   !fRc
    855                     && dwErr == ERROR_INSUFFICIENT_BUFFER
    856                     && cbSid > 0)
    857                 {
    858                     dwErr = NO_ERROR;
    859 
    860                     PSID pSid = (PSID)RTMemAlloc(cbSid * sizeof(wchar_t)); /** @todo r=bird: What's the relationship between wchar_t and PSID? */
    861                     AssertPtrReturn(pSid, VERR_NO_MEMORY); /** @todo r=bird: Leaking token handles when we're out of memory...  */
    862 
    863                     PRTUTF16 pwszDomain = NULL;
    864                     if (cchDomain > 0)
    865                     {
    866                         pwszDomain = (PRTUTF16)RTMemAlloc(cchDomain * sizeof(RTUTF16));
    867                         AssertPtrReturn(pwszDomain, VERR_NO_MEMORY); /** @todo r=bird: Leaking token handles when we're out of memory...  */
    868                     }
    869 
    870                     /* Note: Also supports FQDNs! */
    871                     if (   LookupAccountNameW(NULL,            /* lpSystemName */
    872                                               pwszUser,
    873                                               pSid,
    874                                               &cbSid,
    875                                               pwszDomain,
    876                                               &cchDomain,
    877                                               &sidNameUse)
    878                         && IsValidSid(pSid))
    879                     {
    880                         /* Array of process names we want to look for. */
    881                         static const char * const s_papszProcNames[] =
    882                         {
    883 #ifdef VBOX                 /* The explorer entry is a fallback in case GA aren't installed. */
    884                             { "VBoxTray.exe" },
    885 #endif
    886                             { "explorer.exe" },
    887                             NULL
    888                         };
    889                         fFound = rtProcWinFindTokenByProcess(s_papszProcNames, pSid, &hTokenUserDesktop);
    890                     }
    891                     else
    892                         dwErr = GetLastError(); /* LookupAccountNameW() failed. */
    893                     RTMemFree(pSid);
    894                     RTMemFree(pwszDomain);
    895                 }
    896             }
    897             else /* !RTPROC_FLAGS_SERVICE */
    898             {
    899                 /* Nothing to do here right now. */
    900             }
    901 
    902             /** @todo Hmm, this function already is too big! We need to split
    903              *        it up into several small parts. */
    904 
    905             /* If we got an error due to account lookup/loading above, don't
    906              * continue here. */
    907             if (dwErr == NO_ERROR)
    908             {
    909                 /*
    910                  * If we didn't find a matching VBoxTray, just use the token we got
    911                  * above from LogonUserW(). This enables us to at least run processes with
    912                  * desktop interaction without UI.
    913                  */
    914                 phToken = fFound ? &hTokenUserDesktop : &hTokenLogon;
    915                 RTLDRMOD hUserenv;
    916                 int rc = RTLdrLoad("Userenv.dll", &hUserenv);
    917                 if (RT_SUCCESS(rc))
    918                 {
    919                     PFNLOADUSERPROFILEW pfnLoadUserProfileW;
    920                     rc = RTLdrGetSymbol(hUserenv, "LoadUserProfileW", (void**)&pfnLoadUserProfileW);
    921                     if (RT_SUCCESS(rc))
    922                     {
    923                         PFNUNLOADUSERPROFILE pfnUnloadUserProfile;
    924                         rc = RTLdrGetSymbol(hUserenv, "UnloadUserProfile", (void**)&pfnUnloadUserProfile);
    925                         if (RT_SUCCESS(rc))
    926                         {
    927                             PROFILEINFOW profileInfo;
    928                             if (!(fFlags & RTPROC_FLAGS_NO_PROFILE))
    929                             {
    930                                 RT_ZERO(profileInfo);
    931                                 profileInfo.dwSize = sizeof(profileInfo);
    932                                 profileInfo.lpUserName = pwszUser;
    933                                 profileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */
    934 
    935                                 if (!pfnLoadUserProfileW(*phToken, &profileInfo))
    936                                     dwErr = GetLastError();
    937                             }
    938 
    939                             if (dwErr == NO_ERROR)
    940                             {
    941                                 PRTUTF16 pwszzBlock;
    942                                 rc = rtProcWinCreateEnvFromToken(*phToken, hEnv, &pwszzBlock);
    943                                 if (RT_SUCCESS(rc))
    944                                 {
    945                                     /*
    946                                      * Useful KB articles:
    947                                      *      http://support.microsoft.com/kb/165194/
    948                                      *      http://support.microsoft.com/kb/184802/
    949                                      *      http://support.microsoft.com/kb/327618/
    950                                      */
    951                                     fRc = CreateProcessAsUserW(*phToken,
    952                                                                pwszExec,
    953                                                                pwszCmdLine,
    954                                                                NULL,         /* pProcessAttributes */
    955                                                                NULL,         /* pThreadAttributes */
    956                                                                TRUE,         /* fInheritHandles */
    957                                                                dwCreationFlags,
    958                                                                pwszzBlock,
    959                                                                NULL,         /* pCurrentDirectory */
    960                                                                pStartupInfo,
    961                                                                pProcInfo);
    962                                     if (fRc)
    963                                         dwErr = NO_ERROR;
    964                                     else
    965                                         dwErr = GetLastError(); /* CreateProcessAsUserW() failed. */
    966                                     rtProcWinDestoryEnv(pwszzBlock);
    967                                 }
    968 
    969                                 if (!(fFlags & RTPROC_FLAGS_NO_PROFILE))
    970                                 {
    971                                     fRc = pfnUnloadUserProfile(*phToken, profileInfo.hProfile);
    972 #ifdef RT_STRICT
    973                                     if (!fRc)
    974                                     {
    975                                         DWORD dwErr2 = GetLastError();
    976                                         AssertMsgFailed(("Unloading user profile failed with error %u (%#x) - Are all handles closed? (dwErr=%u)",
    977                                                          dwErr2, dwErr2, dwErr));
    978                                     }
    979 #endif
    980                                 }
    981                             }
    982                         }
    983                     }
    984                     RTLdrClose(hUserenv);
    985                 } /* Userenv.dll found/loaded? */
    986             } /* Account lookup succeeded? */
    987             if (hTokenUserDesktop != INVALID_HANDLE_VALUE)
    988                 CloseHandle(hTokenUserDesktop);
    989             rtProcWinUserLogoff(hTokenLogon);
    990         }
    991     }
    992 
    993     if (   RT_SUCCESS(rc)
    994         && dwErr != NO_ERROR)
    995         rc = rtProcWinMapErrorCodes(dwErr);
     1012    if (RT_FAILURE(rc))
     1013        rc = rtProcWinCreateAsUser2(pwszUser, pwszPassword, pwszExec, pwszCmdLine, hEnv, dwCreationFlags, pStartupInfo, pProcInfo, fFlags);
    9961014
    9971015    return rc;
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