 |
|
 |
Love the work put into this class. However, in my not-so-usual circumstance it does not appear to fully work. I am on Domain A, trying to run a process on the PC logged into Domain A as a user authenticated from Domain B (No trust relationships in place). It works to trigger notepad, etc just fine as intended. However, we have an Encryption software password reset tool we run that requires the authentication against Domain B when it is ran. When using the built-in command runas.exe with windows this works as intended, but not with this class or other methods I have tried (also must be using the netonly in both cases).
Do you have any insight as to why this may be the case where runas.exe in windows works, but your class or similar methods do not work? (My own work is nearly identical to yours, just not as indepth as it's written specifically for the netonly and all.) The reason to use your method or mine is to bypass the entire console window logon process and allow the end user to provide the credentials in a login box and then start the application. Using runas, we must have them login there (Using AutoIT, don't as me why, I didn't write that lol) to check password expiry etc, then login with runas.exe as well to trigger the application.
The reason for the rewrite of our system in place (other than the obvious) is due to unknown updates certain machines in WinXP no longer work with these methods and windows 7 is not working as well. I am trying to redo the entire process in C# to centralize it and using methods that "should not" change like that. Unfortunately I haven't had much experience with Active Directory as of yet, and that is what all these accounts are in. Running in ad.domain.com, simplified domainname\user.
|
|
|
|
 |
|
 |
I honestly haven't used or looked at this for probably 4-5 years so I'm not certain how the APIs have changed that might have affected this. I'm sorry I can't be of any help, but this was written almost 6 years ago.
Dewey Vozel
|
|
|
|
 |
|
 |
Hey, thanks very much for the article.
Your code was great in that it confirmed I was correct in my own implementation, but more importantly you clued me up to the fact that all I needed was to ensure the Secondary Logon service was included in the XP Embedded image I'm working with.
Thanks again - you got my 5
Kev
|
|
|
|
 |
|
 |
Glad it helped. I didn't figure anyone needed this anymore!
Dewey Vozel
|
|
|
|
 |
|
 |
I want to run the program when i type the username and password and than press the enter key.
How can i do this ?
I'm a dummy in C# and tried a lot of things.
One of them is to set a Acceptbutton, but you can only do that in the Form.
Can anyone help me pls.
Regards,
Ilker
|
|
|
|
 |
|
 |
public Class RunAs {
[DllImport("kernel32.dll", SetLastError = true)]
public static extern UInt32 WaitForSingleObject
(
IntPtr hHandle,
UInt32 dwMilliseconds
);
....
}
Process P = RunAs.StartProcess(......
);
UInt32 Result = RunAs.WaitForSingleObject(P.Handle, 0xFFFFFFFF);
if (Result==0xFFFFFFFF) { //Error .... }
|
|
|
|
 |
|
 |
I tried to run your nice demo. Unfortunately I get always error "The service cannot be started, either because it is disabled or because it has not enabled devices associated with it".
I am running demo with administrator user.
Is this a new "security" patch or someting...
Best Regards,
Jake
|
|
|
|
 |
|
 |
It seems to start program in other PC that have logged power user account and I get to demo administrator rigths.
The program try to load ipfltdrv driver but it get still error in loading. Do you have any idea to fix it???
Jake
|
|
|
|
 |
|
 |
Look in your Windows Services (start>run>services.msc) and you should see a Microsoft service (I think it's actually called Runas) that has to be enabled and started or else the WinAPI that my class uses is not available.
Dewey Vozel
|
|
|
|
 |
|
 |
Thank you very much! The service is Secondary Logon. I have optimized it off
All the Best,
Jake
|
|
|
|
 |
|
 |
Just Another Info ,in Windows 2000 the service is "RunAs" , in Windows 2003 /XP the service is "Secondary Logon"
Thanks
Naresh
|
|
|
|
 |
|
 |
meaning i have a machine with a local administrator login to it and not a domain user (MyMachineName\admin). can i runas it and use the domain? how can i do it?
great app!
|
|
|
|
 |
|
 |
I have an asp.net 1.1 app that I need to call an exe from across the network. I need to use something like this... will this work for me?
|
|
|
|
 |
|
 |
I have never tried it. I think that code access security will prevent it though. I'm not into ASP.NET though, so I could be mistaken.
-=( atchr )=-
|
|
|
|
 |
|
 |
This is a great class. I kept getting "...failed to init properly (0xc0000142)" error when using System.Diagnotics.Process directly. I found thru another source that I needed to set the StartInfo.lpDesktop = @"winsta0\default". This solution solved that major headache, however, I still need the stream redirection of the Process class. Anyone taken the trouble to do that or would know how? I don't see it.
Thanks,
Dave
|
|
|
|
 |
|
 |
Thanks for the feedback. I started working on a class but because of my schedule it has been sitting around 80% complete like many of my other solutions that I'd love to share.
Hopefully I'll get to finish some of them before they are made obsolete by some point-and-click features that will be added in future versions of VS.NET and the dotnetfrx to make it even easier for 10 year olds to write software without understanding any underlying concepts. (not pointed at you...just venting...the doctors say it's healthy)
-=( atchr )=-
|
|
|
|
 |
|
 |
I don't see any mention of the license that you wish to release the source code under. I was wondering whether you were releaseing it into the public domain or under a bsd style license or some other more restrictive license?
|
|
|
|
 |
|
 |
You can use it, I'd appreciate if you leave the author information in the files...but other than that I don't care for now.
-=( atchr )=-
|
|
|
|
 |
|
 |
I am running a service with desktop interaction under an administrator account. I can't use CreateProcessWithLogonW under the System account because it's not allowed in SP2 and in W2003.
I get the following error when trying to run notepad:
The application failed to initialize properly (0xc0000142). Click on OK to terminate the application. The process lies in the task manager with the right username but exits as soon as I click ok.
I am only using username, domain, password and commandline. Do I have to specify anything more to create a window?
thanks,
Henrik
|
|
|
|
 |
|
 |
See my last post of a few minutes ago. I'm wanting to spawn processes from a Windows Service also. I found I needed to set the desktop member of StartInfo struct.
Dave
|
|
|
|
 |
|
 |
EyeHatePickingScreenNames wrote: I'm wanting to spawn processes from a Windows Service also. I found I needed to set the desktop member of StartInfo struct.
I've encountered another problem: my Windows Service starts a process which needs access to a network share. This process uses the correct username an domain but the networking rights got lost. If the process is started manually everything works fine.
Any ideas?
Thanks,
Martin
|
|
|
|
 |
|
 |
While very useful in leading me down the path, I didn't wind up using Dewey's code as is. I later found that CreateProcessWithLogon doesn't honor the lpDesktop member of the startup info which needs to be "" in this scenario. So, you have to use LogonUser, DuplicateTokenEx, LoadUserProfile (depending on app being started), and finally, CreateProcessAsUser.
If you do all that and your process still doesn't behave as if running from the command prompt, then something else is wrong. I routinely start processes that access the network. For example, copying files to shares and running rsh/ssh commands to Unix boxes. It was a long road getting there tho.
Dave
|
|
|
|
 |
|
 |
davelogie wrote: I later found that CreateProcessWithLogon doesn't honor the lpDesktop member of the startup info which needs to be "" in this scenario. So, you have to use LogonUser, DuplicateTokenEx, LoadUserProfile (depending on app being started), and finally, CreateProcessAsUser.
Thanks a lot for that tip. Please could you post that piece of code?
Martin
|
|
|
|
 |
|
 |
Here's logon stuff. You can add CreateProcessAsUser easy enough. Remember that the lpDesktop member of StartupInfo = "" (not NULL and not "WinSta0\Default") for this application. Good luck.
#region User Logon
///
/// The LogonUser function attempts to log a user on to the local computer.
///
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool LogonUser(String lpszUsername, String lpszDomain, IntPtr lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr hToken);
///
/// The DuplicateTokenEx function creates a new access token that duplicates an existing token. This function can create either a primary token or an impersonation token.
///
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DuplicateTokenEx(IntPtr hExistingToken, int dwDesiredAccess, ref SecurityAttributes lpTokenAttributes,
int impersonationLevel, int tokenType, out IntPtr phNewToken);
///
/// The LoadUserProfile function loads the specified user's profile
///
[DllImport("userenv.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool LoadUserProfile(IntPtr hToken, ref ProfileInfo lpProfileInfo);
///
/// The UnloadUserProfile function unloads a user's profile that was loaded by the LoadUserProfile function
///
[DllImport("userenv.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool UnloadUserProfile(IntPtr hToken, IntPtr hProfile);
///
/// Closes an open object handle.
///
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool CloseHandle(IntPtr handle);
///
/// The SECURITY_ATTRIBUTES structure contains the security descriptor for an object and specifies whether the handle retrieved by specifying this structure is inheritable
///
[StructLayout(LayoutKind.Sequential)]
public struct SecurityAttributes
{
public int dwLength;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
///
/// Profile Info
///
[StructLayout(LayoutKind.Sequential)]
public struct ProfileInfo
{
///
/// Specifies the size of the structure, in bytes.
///
public int dwSize;
///
/// This member can be one of the following flags: PI_NOUI or PI_APPLYPOLICY
///
public int dwFlags;
///
/// Pointer to the name of the user.
/// This member is used as the base name of the directory in which to store a new profile.
///
public string lpUserName;
///
/// Pointer to the roaming user profile path.
/// If the user does not have a roaming profile, this member can be NULL.
///
public string lpProfilePath;
///
/// Pointer to the default user profile path. This member can be NULL.
///
public string lpDefaultPath;
///
/// Pointer to the name of the validating domain controller, in NetBIOS format.
/// If this member is NULL, the Windows NT 4.0-style policy will not be applied.
///
public string lpServerName;
///
/// Pointer to the path of the Windows NT 4.0-style policy file. This member can be NULL.
///
public string lpPolicyPath;
///
/// Handle to the HKEY_CURRENT_USER registry key.
///
public IntPtr hProfile;
}
///
/// Logon type option.
///
[FlagsAttribute]
public enum LogonType
{
///
/// This logon type is intended for users who will be interactively using the computer
///
Interactive = 2,
///
/// This logon type is intended for high performance servers to authenticate plaintext passwords.
///
Network = 3,
///
/// This logon type is intended for batch servers, where processes may be executing on behalf of a user without their direct intervention.
///
Batch = 4,
///
/// Indicates a service-type logon. The account provided must have the service privilege enabled.
///
Service = 5,
///
/// This logon type is for GINA DLLs that log on users who will be interactively using the computer.
///
Unlock = 7
}
///
/// Specifies the logon provider.
///
[FlagsAttribute]
public enum LogonProvider
{
///
/// Use the standard logon provider for the system.
///
Default = 0,
///
/// Use the negotiate logon provider. (WINNT50)
///
Negotiate = 3,
///
/// Use the NTLM logon provider (WINNT40)
///
NTLM = 2,
///
/// Use the Windows NT 3.5 logon provider.
///
WinNT35 = 1
}
///
/// Specifies the requested access rights for the new token.
///
[FlagsAttribute]
public enum DuplicateTokenDesiredAccess
{
///
/// To request the same access rights as the existing token, specify zero.
///
SameAsExisting = 0,
///
/// To request all access rights that are valid for the caller, specify MAXIMUM_ALLOWED.
///
MaximumAllowed = 0x02000000
}
///
/// Specifies a value from the SECURITY_IMPERSONATION_LEVEL enumeration that indicates the impersonation level of the new token
///
[FlagsAttribute]
public enum ImpersonationLevel
{
///
/// The server process cannot obtain identification information about the client, and it cannot impersonate the client. It is defined with no value given, and thus, by ANSI C rules, defaults to a value of zero.
///
Anonymous = 0,
///
/// The server process can obtain information about the client, such as security identifiers and privileges, but it cannot impersonate the client. This is useful for servers that export their own objects, for example, database products that export tables and views. Using the retrieved client-security information, the server can make access-validation decisions without being able to use other services that are using the client's security context.,
///
Identification = 1,
///
/// The server process can impersonate the client's security context on its local system. The server cannot impersonate the client on remote systems.,
///
Impersonation = 2,
///
/// The server process can impersonate the client's security context on remote systems. This impersonation level is not supported on WinNT
///
Delegation = 3
}
///
/// Specifies the requested access rights for the new token.
///
[FlagsAttribute]
public enum TokenType
{
///
/// The new token is a primary token that you can use in the CreateProcessAsUser function.
///
Primary = 1,
///
/// The new token is an impersonation token.
///
Impersonation = 2
}
private void LogonUser(String user, String domain, SecureString password, LogonType type, LogonProvider provider)
{
if (password.IsReadOnly() == false)
throw new InvalidOperationException("SecureString not ReadOnly");
if (string.IsNullOrEmpty(user) == true || string.IsNullOrEmpty(domain) == true)
throw new InvalidOperationException("No user account specified");
IntPtr handle;
IntPtr bstr = Marshal.SecureStringToBSTR(password);
bool result = LogonUser(user, domain, bstr, (int)type, (int)provider, out handle);
Marshal.ZeroFreeBSTR(bstr);
if (result == false)
throw new System.ComponentModel.Win32Exception();
SecurityAttributes sa = new SecurityAttributes();
sa.dwLength = Marshal.SizeOf(sa);
sa.lpSecurityDescriptor = IntPtr.Zero;
sa.bInheritHandle = true;
IntPtr newHandle;
result = DuplicateTokenEx(handle, (int)DuplicateTokenDesiredAccess.MaximumAllowed, ref sa,
(int)ImpersonationLevel.Impersonation, (int)TokenType.Primary, out newHandle);
if (result == false)
throw new System.ComponentModel.Win32Exception();
CloseHandle(handle);
handle = newHandle;
hToken = handle;
}
public void LoadUserProfile(string username)
{
if (hToken == IntPtr.Zero)
throw new InvalidOperationException("User not logged in");
ProfileInfo info = new ProfileInfo();
info.dwSize = Marshal.SizeOf(info);
info.lpUserName = username;
info.dwFlags = 1; // PI_NOUI 0x00000001 // Prevents displaying of messages
bool result = LoadUserProfile(hToken, ref info);
if (result == false)
throw new System.ComponentModel.Win32Exception();
hProfile = info.hProfile;
}
internal void LogOffUser()
{
#if false
string identity = WindowsIdentity.GetCurrent().Name;
string threadIdentity = Thread.CurrentPrincipal.Identity.Name;
scriptTask.State.AddMessage(DateTime.Now, string.Format("Logging off user {0} ({1})", identity, threadIdentity));
#endif
WindowsIdentity.Impersonate(IntPtr.Zero);
#if false
identity = WindowsIdentity.GetCurrent().Name;
Thread.CurrentPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
threadIdentity = Thread.CurrentPrincipal.Identity.Name;
scriptTask.State.AddMessage(DateTime.Now, string.Format("Identity now {0} ({1})", identity, threadIdentity));
#endif
if (hToken != IntPtr.Zero && hProfile != IntPtr.Zero)
{
bool result = UnloadUserProfile(hToken, hProfile);
hProfile = IntPtr.Zero;
if (result == false)
throw new System.ComponentModel.Win32Exception();
}
if (hToken != IntPtr.Zero)
{
bool result = CloseHandle(hToken);
hToken = IntPtr.Zero;
if (result == false)
throw new System.ComponentModel.Win32Exception();
}
}
#endregion // User Logon
|
|
|
|
 |
|
 |
Hi davelogie,
With your solution I got the same problems - still no network access.
Am I using wrong parameters?
public static System.Diagnostics.Process StartProcessDesktop(string userName,
string domain, string password, string commandLine, short showWindow,
string sCurrentDirectory)
{
bool retval;
StringBuilder cl = new StringBuilder(commandLine.Length);
cl.Append(commandLine);
retval = LogonUser(userName, domain, password, LogonType.Interactive, LogonProvider.Default);
if (!retval)
{
throw new System.ComponentModel.Win32Exception();
}
retval = LoadUserProfile(userName);
if (!retval)
{
throw new System.ComponentModel.Win32Exception();
}
SecurityAttributes saproc = new SecurityAttributes();
saproc.dwLength = Marshal.SizeOf(saproc);
saproc.lpSecurityDescriptor = IntPtr.Zero;
saproc.bInheritHandle = true;
SecurityAttributes sathread = new SecurityAttributes();
sathread.dwLength = Marshal.SizeOf(sathread);
sathread.lpSecurityDescriptor = IntPtr.Zero;
sathread.bInheritHandle = true;
ProcessInformation processInfo;
StartUpInfo startupInfo = new StartUpInfo();
startupInfo.cb = Marshal.SizeOf(startupInfo);
startupInfo.lpTitle = null;
startupInfo.lpDesktop = "";
startupInfo.dwFlags = (int)StartUpInfoFlags.UseShowWindow;
startupInfo.wShowWindow = showWindow;
retval = CreateProcessAsUser(_hToken, null, cl, ref saproc, ref sathread, false, (uint)(CreationFlags.NewProcessGroup | CreationFlags.NewConsole), IntPtr.Zero,
sCurrentDirectory, ref startupInfo, out processInfo);
if (!retval)
{
throw new System.ComponentModel.Win32Exception();
}
else
{
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
return System.Diagnostics.Process.GetProcessById(processInfo.dwProcessId);
}
}
Thanks,
Martin
|
|
|
|
 |