- Timestamp:
- Jan 25, 2012 2:44:50 AM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/r3/win/process-win.cpp
r39866 r39867 699 699 /* If we don't have the Userenv-API for whatever reason or something with the 700 700 * 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. */ 701 702 if (RT_FAILURE(rc)) 702 703 rc = RTEnvQueryUtf16Block(hEnv, ppwszBlock); … … 745 746 746 747 748 /** 749 * Method \#2. 750 */ 751 static 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 */ 957 static 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 747 996 static int rtProcWinCreateAsUser(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine, 748 997 RTENV hEnv, DWORD dwCreationFlags, 749 998 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, uint32_t fFlags) 750 999 { 751 int rc = VINF_SUCCESS;752 BOOL fRc = FALSE;753 DWORD dwErr = NO_ERROR;754 755 1000 /* 756 1001 * If we run as a service CreateProcessWithLogon will fail, 757 1002 * so don't even try it (because of Local System context). 758 1003 */ 1004 int rc = VERR_TRY_AGAIN; 759 1005 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); 798 1007 799 1008 /* … … 801 1010 * we're running as a Windows service? 802 1011 */ 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); 996 1014 997 1015 return rc;
Note:
See TracChangeset
for help on using the changeset viewer.