@@ -27,10 +27,11 @@ namespace PSCloudbase
2727 {
2828 const int LOGON32_LOGON_SERVICE = 5;
2929 const int LOGON32_PROVIDER_DEFAULT = 0;
30-
30+ const int TOKEN_ALL_ACCESS = 0x000f01ff;
3131 const uint GENERIC_ALL_ACCESS = 0x10000000;
32-
3332 const uint INFINITE = 0xFFFFFFFF;
33+ const uint PI_NOUI = 0x00000001;
34+ const uint WAIT_FAILED = 0xFFFFFFFF;
3435
3536 enum SECURITY_IMPERSONATION_LEVEL
3637 {
@@ -86,6 +87,57 @@ namespace PSCloudbase
8687 public IntPtr hStdError;
8788 }
8889
90+ [StructLayout(LayoutKind.Sequential)]
91+ struct PROFILEINFO {
92+ public int dwSize;
93+ public uint dwFlags;
94+ [MarshalAs(UnmanagedType.LPTStr)]
95+ public String lpUserName;
96+ [MarshalAs(UnmanagedType.LPTStr)]
97+ public String lpProfilePath;
98+ [MarshalAs(UnmanagedType.LPTStr)]
99+ public String lpDefaultPath;
100+ [MarshalAs(UnmanagedType.LPTStr)]
101+ public String lpServerName;
102+ [MarshalAs(UnmanagedType.LPTStr)]
103+ public String lpPolicyPath;
104+ public IntPtr hProfile;
105+ }
106+
107+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
108+ public struct USER_INFO_4
109+ {
110+ public string name;
111+ public string password;
112+ public int password_age;
113+ public uint priv;
114+ public string home_dir;
115+ public string comment;
116+ public uint flags;
117+ public string script_path;
118+ public uint auth_flags;
119+ public string full_name;
120+ public string usr_comment;
121+ public string parms;
122+ public string workstations;
123+ public int last_logon;
124+ public int last_logoff;
125+ public int acct_expires;
126+ public int max_storage;
127+ public int units_per_week;
128+ public IntPtr logon_hours; // This is a PBYTE
129+ public int bad_pw_count;
130+ public int num_logons;
131+ public string logon_server;
132+ public int country_code;
133+ public int code_page;
134+ public IntPtr user_sid; // This is a PSID
135+ public int primary_group_id;
136+ public string profile;
137+ public string home_dir_drive;
138+ public int password_expired;
139+ }
140+
89141 [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
90142 extern static bool DuplicateTokenEx(
91143 IntPtr hExistingToken,
@@ -133,14 +185,31 @@ namespace PSCloudbase
133185 static extern bool GetExitCodeProcess(IntPtr hProcess,
134186 out uint lpExitCode);
135187
188+ [DllImport("userenv.dll", SetLastError=true, CharSet=CharSet.Auto)]
189+ [return: MarshalAs(UnmanagedType.Bool)]
190+ static extern bool LoadUserProfile(IntPtr hToken,
191+ ref PROFILEINFO lpProfileInfo);
192+
193+ [DllImport("userenv.dll", SetLastError=true, CharSet=CharSet.Auto)]
194+ [return: MarshalAs(UnmanagedType.Bool)]
195+ static extern bool UnloadUserProfile(IntPtr hToken, IntPtr hProfile);
196+
197+ [DllImport("Netapi32.dll", CharSet=CharSet.Unicode, ExactSpelling=true)]
198+ extern static int NetUserGetInfo(
199+ [MarshalAs(UnmanagedType.LPWStr)] string ServerName,
200+ [MarshalAs(UnmanagedType.LPWStr)] string UserName,
201+ int level, out IntPtr BufPtr);
202+
136203 public static uint RunProcess(string userName, string password,
137204 string domain, string cmd,
138- string arguments)
205+ string arguments,
206+ bool loadUserProfile = true)
139207 {
140208 bool retValue;
141209 IntPtr phToken = IntPtr.Zero;
142210 IntPtr phTokenDup = IntPtr.Zero;
143211 PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION();
212+ PROFILEINFO pi = new PROFILEINFO();
144213
145214 try
146215 {
@@ -164,17 +233,35 @@ namespace PSCloudbase
164233 STARTUPINFO sInfo = new STARTUPINFO();
165234 sInfo.lpDesktop = "";
166235
236+ if(loadUserProfile)
237+ {
238+ IntPtr userInfoPtr = IntPtr.Zero;
239+ int retValueNetUser = NetUserGetInfo(null, userName, 4,
240+ out userInfoPtr);
241+ if(retValueNetUser != 0)
242+ throw new Win32Exception(retValueNetUser);
243+
244+ USER_INFO_4 userInfo = (USER_INFO_4)Marshal.PtrToStructure(
245+ userInfoPtr, typeof(USER_INFO_4));
246+
247+ pi.dwSize = Marshal.SizeOf(pi);
248+ pi.dwFlags = PI_NOUI;
249+ pi.lpUserName = userName;
250+ pi.lpProfilePath = userInfo.profile;
251+
252+ retValue = LoadUserProfile(phTokenDup, ref pi);
253+ if(!retValue)
254+ throw new Win32Exception(GetLastError());
255+ }
256+
167257 retValue = CreateProcessAsUser(phTokenDup, cmd, arguments,
168258 ref sa, ref sa, false, 0,
169259 IntPtr.Zero, null,
170260 ref sInfo, out pInfo);
171261 if(!retValue)
172262 throw new Win32Exception(GetLastError());
173263
174- WaitForSingleObject(pInfo.hProcess, INFINITE);
175-
176- var lastErr = GetLastError();
177- if(lastErr != 0)
264+ if(WaitForSingleObject(pInfo.hProcess, INFINITE) == WAIT_FAILED)
178265 throw new Win32Exception(GetLastError());
179266
180267 uint exitCode;
@@ -186,6 +273,8 @@ namespace PSCloudbase
186273 }
187274 finally
188275 {
276+ if(pi.hProfile != IntPtr.Zero)
277+ UnloadUserProfile(phTokenDup, pi.hProfile);
189278 if(phToken != IntPtr.Zero)
190279 CloseHandle(phToken);
191280 if(phTokenDup != IntPtr.Zero)
@@ -212,7 +301,10 @@ function Start-ProcessAsUser
212301 [string ]$Arguments ,
213302
214303 [parameter (Mandatory = $true )]
215- [PSCredential ]$Credential
304+ [PSCredential ]$Credential ,
305+
306+ [parameter ()]
307+ [bool ]$LoadUserProfile = $true
216308 )
217309 process
218310 {
@@ -226,6 +318,6 @@ function Start-ProcessAsUser
226318
227319 [PSCloudbase.ProcessManager ]::RunProcess($nc.UserName , $nc.Password ,
228320 $domain , $Command ,
229- $Arguments )
321+ $Arguments , $LoadUserProfile )
230322 }
231323}
0 commit comments