I have developed an application in c# using visual studio 2015, that copies some files from one directory (source) to another (destination) in general.
My problem is that the source path is another computer in a domain. I wish to be able to access the directory and get my files, user the domain, username and password the source computer.
I have seen some solution, but I can't get how they access the other computer.
I used to get my files by using the Directory. GetDirectories (path), and I'm far too deep using it now and can't change it to smooth as.
thanks for helping me with my problem I'm truly blocked for days now.
string[] folder1;
string[] folder2;
folder1 = Directory.GetDirectories(path);
foreach (string fld1 in folder1)
{
folder2 = Directory.GetDirectories(fld);
foreach(string fld2 in folder2)
{
for(int i = 0; i < MyList.Count(); i++)
{
if(fld2.Contains("nok") || fld2.Contains("Nok"))
LNok = Directory.GetFiles(fld2, picList[i]);
else
Lok = Directory.GetFiles(fld2, picList[i]);
}
}
}
Since System.IO.File and System.IO.Directory methods don’t support passing credentials, the preferred solution is to impersonate an authorized user account. This requires to import two methods from the advapi32.dll and kernel32.dll using pInvoke:
//Impersonation functionality
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
//Disconnection after file operations
[DllImport("kernel32.dll")]
private static extern Boolean CloseHandle(IntPtr hObject);
The following code makes use of those methods to create a WindowsImpersonationContext
const int LOGON_TYPE_NEW_CREDENTIALS = 9;
const int LOGON32_PROVIDER_WINNT50 = 3;
//User token that represents the authorized user account
IntPtr token = IntPtr.Zero;
bool result = LogonUser("username", "domainname", "password", LOGON_TYPE_NEW_CREDENTIALS , LOGON32_PROVIDER_WINNT50, ref token);
if (result == true)
{
//Use token to setup a WindowsImpersonationContext
using (WindowsImpersonationContext ctx = new WindowsIdentity(token).Impersonate())
{
//Your file operations
string[] files = Directory.GetFiles(#"\\remotemachine\share\folder");
//Release the context, and close user token
ctx.Undo();
CloseHandle(token);
}
}
Here you may find the MSDN documentation of the LogonUser function
Related
I am using windows functionality, impersonate, to change the active user before starting Chrome driver. Now, starting the driver without the impersonate code works fine. Also the impersonate code works fine; I see the user is changed. But when this change happens and after that I run IWebDriver driver=new ChromeDriver then the exception is triggered on that exact code and the test stops. Any ideas why this happens?
Here is main part of the code used (the code is just little modified code from another post here at stackoverflow)
namespace localSeleniumTest.Impersonation
{
class Program
{
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(stringpszUsername, string pszDomain, string pszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
// closes open handes returned by LogonUser
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
public void Impersonation()
{
WindowsImpersonationContext impersonationContext = null;
IntPtr userHandle = IntPtr.Zero;
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
string domain = Config.domain;
string user = Config.username;
string password = Config.password;
try
{
String currentName = WindowsIdentity.GetCurrent().Name;
// if domain name was blank, assume local machine
if (domain == "")
domain = System.Environment.MachineName;
// Call LogonUser to get a token for the user
bool loggedOn = LogonUser(
user,
domain,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref userHandle
);
if (!loggedOn)
{
return;
}
// Begin impersonating the user
impersonationContext = WindowsIdentity.Impersonate(userHandle);
String afterImpersonationName = WindowsIdentity.GetCurrent().Name;
/*this few lines below does not work after impersonation but
work perfectly without the code above.*/
IWebDriver driver = new ChromeDriver();
driver.Navigate().GoToUrl("www.google.com");
System.Threading.Thread.Sleep(6000);
driver.Quit();
Found the issue. D user did not have permission to chrome driver i Bin folder
I need to log on my controller to another machine and copy a file on it; I have to use a local user on the remote machine.
Currently I'm using this code:
private Impersonate(bool active, string domain, string username, string password, LogonType logonType)
{
if (active)
{
IntPtr handle;
var ok = NativeMethods.LogonUser(username, domain, password, (int)logonType, 0, out handle);
if (!ok)
{
var errorCode = Marshal.GetLastWin32Error();
throw new ApplicationException(string.Format("Could not impersonate the elevated user. LogonUser returned error code {0}.", errorCode));
}
_handle = new SafeTokenHandle(handle);
_context = WindowsIdentity.Impersonate(_handle.DangerousGetHandle());
}
}
passing these args:
using (Impersonate.LogonUser(true,
".",
"todev1.domain.com\admin",
"Test123_",
LogonType.Interactive))
{
}
and this win API:
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);
I checked this Q/A Using advapi32.dll:LogonUserA() to impersonate a remote machine's local user but the solution provided is not working.
I tried to pass multiple values to the method as domain, user and so on, but I can't find the rigth solution. I tried using NewCredentials but it returns always ok even if not logged.
I finally solved this issue, without the need to add the user to each machine that will impersonate to the remote machine.
It's correct to use NewCredential, but using the WINNT50 LogonProvider.
So my impersonate method now is like:
private Impersonate(bool active, string domain, string username, string password, LogonType logonType, LogonProvider logonProvider)
{
if (active)
{
IntPtr handle;
var ok = NativeMethods.LogonUser(username, domain, password, (int)logonType, (int)logonProvider, out handle);
if (!ok)
{
var errorCode = Marshal.GetLastWin32Error();
throw new ApplicationException(string.Format("Could not impersonate the elevated user. LogonUser returned error code {0}.", errorCode));
}
_handle = new SafeTokenHandle(handle);
_context = WindowsIdentity.Impersonate(_handle.DangerousGetHandle());
}
}
Then I call the Impersonate method using the code:
using (Impersonate.LogonUser(true,
"todev1.domain.com",
"admin",
"Test123_",
LogonType.NewCredentials,
LogonProvider.WinNT50))
{
}
You can try to make a local user on your local machine with the same username and password as the local user on the remote server.
I've spent a few days on this problem and even though there are tons of different examples online it's a tricky problem and I can't get them to work in my scenario.
I have a Windows service that runs under the Local System account. It has a WCF endpoint listening to API requests. When told via the API, the service is supposed to start a new process in the System session (0) and with the "Worker" account credentials. The process is a worker that checks for work in a queue and does it. If it does not find work, it will sleep for a bit and check again. If it does find work, it starts a new process in the same session and with the same credentials and does the work. After it's done the work it closes.
The "Worker" is a domain account and a member of the local administrators group on the machine, which has execute permissions on the executable. The machine is on the same domain as the account.
The problem is that when the service tries to start the process it gets a ERROR_ACCESS_DENIED (5) error code from the CreateProcessAsUser method.
I tried running the same code on a Windows 7 machine with the same credentials and it works fine, but it gets that error code when running on Windows Server 2008.
The code's too big to show here, so I've put it elsewhere...
ProcessHelper: http://pastie.org/private/y7idu3nw4xv1fxzeizbn9g
The service calls the StartAsUserFromService method to start the process, which internally calls CreateProcessAsUser after establishing a session. The process calls the StartAsUserFromApplication method to start its successor, which internally calls CreateProcessWithLogonW.
ImpersonationContext: http://pastie.org/private/xppc7wnoidajmpq8h8sg
The service needs to get the user token to start a process as them. The process doesn't need that to start its successor. As far as I can tell the impersonation is successful on Server 2008, but it doesn't have some permissions, and I can't figure out which.
EDIT:
I tried both a local administrator account and a domain account on the Windows 7 machine, and they work fine. But neither of them work on the Server 2008 machine. There must be a permission missing somewhere, but I don't know where; the error message isn't helpful.
I also tried ticking the "run as administrator" box in the compatibility tab of the executable, but it made no difference.
EDIT:
I used process monitor to see what's going on in the service and this is where it's getting the error...
Date & Time: 12/02/2014 11:44:03
Event Class: File System
Operation: CreateFile
Result: ACCESS DENIED
Path: D:\..\executable.exe
TID: 6244
Duration: 0.0000450
Desired Access: Read Data/List Directory, Execute/Traverse, Read Attributes, Synchronize
Disposition: Open
Options: Synchronous IO Non-Alert, Non-Directory File
Attributes: n/a
ShareMode: Read, Delete
AllocationSize: n/a
Impersonating: Domain\Worker
and
Date & Time: 12/02/2014 11:44:03
Event Class: File System
Operation: CreateFile
Result: ACCESS DENIED
Path: D:\..\executable.exe
TID: 6244
Duration: 0.0000480
Desired Access: Execute/Traverse, Synchronize
Disposition: Open
Options: Synchronous IO Non-Alert, Non-Directory File
Attributes: n/a
ShareMode: Read, Delete
AllocationSize: n/a
Impersonating: Domain\Worker
Some tips :
How to Impersonate
Impersonation code in C#
Impersonation Libraries (Class & Com Class)
WindowsIdentity.Impersonate Method
Try using this sample (found this somewhere) :
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;
[assembly:SecurityPermissionAttribute(SecurityAction.RequestMinimum,UnmanagedCode=true)]
[assembly:PermissionSetAttribute(SecurityAction.RequestMinimum, Name ="FullTrust")]
public class ImpersonationDemo
{
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
public enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
public enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private unsafe static extern int FormatMessage(int dwFlags, ref IntPtr lpSource, int dwMessageId, int dwLanguageId, ref String lpBuffer, int nSize, IntPtr *Arguments);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError=true)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateTokenEx( IntPtr hExistingToken, uint dwDesiredAccess, ref SECURITY_ATTRIBUTES lpTokenAttributes,
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
TOKEN_TYPE TokenType,
out IntPtr phNewToken);
// GetErrorMessage formats and returns an error message
// corresponding to the input errorCode.
public unsafe static string GetErrorMessage(int errorCode)
{
int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
int messageSize = 255;
String lpMsgBuf = "";
int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
IntPtr ptrlpSource = IntPtr.Zero;
IntPtr prtArguments = IntPtr.Zero;
int retVal = FormatMessage(dwFlags, ref ptrlpSource, errorCode, 0, ref lpMsgBuf, messageSize, &prtArguments);
if (0 == retVal)
{
throw new Exception("Failed to format message for error code " + errorCode + ". ");
}
return lpMsgBuf;
}
// Test harness.
// If you incorporate this code into a DLL, be sure to demand FullTrust.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public static void Main(string[] args)
{
IntPtr tokenHandle = new IntPtr(0);
IntPtr dupeTokenHandle = new IntPtr(0);
try
{
string UserName, MachineName;
// Get the user token for the specified user, machine, and password using the
// unmanaged LogonUser method.
Console.Write("Enter the name of a machine on which to log on: ");
MachineName = Console.ReadLine();
Console.Write("Enter the login of a user on {0} that you wish to impersonate: ", MachineName);
UserName = Console.ReadLine();
Console.Write("Enter the password for {0}: ", UserName);
const int LOGON32_PROVIDER_DEFAULT = 3;
//This parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 8;
tokenHandle = IntPtr.Zero;
dupeTokenHandle = IntPtr.Zero;
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(UserName, MachineName, "mm4geata",
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref tokenHandle);
Console.WriteLine("LogonUser called.");
if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
Console.WriteLine("LogonUser failed with error code : {0}", ret);
Console.WriteLine("\nError: [{0}] {1}\n", ret, GetErrorMessage(ret));
return;
}
Console.WriteLine("Did LogonUser Succeed? " + (returnValue? "Yes" : "No"));
Console.WriteLine("Value of Windows NT token: " + tokenHandle);
// Check the identity.
Console.WriteLine("Before impersonation: " + WindowsIdentity.GetCurrent().Name);
//bool retVal = DuplicateToken(tokenHandle, SecurityImpersonation, ref dupeTokenHandle);
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.bInheritHandle = true;
sa.Length = Marshal.SizeOf(sa);
sa.lpSecurityDescriptor = (IntPtr)0;
bool retVal = DuplicateTokenEx(tokenHandle, 0x10000000, ref sa, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenImpersonation, out dupeTokenHandle);
if (false == retVal)
{
CloseHandle(tokenHandle);
Console.WriteLine("Exception thrown in trying to duplicate token.");
return;
}
// The token that is passed to the following constructor must
// be a primary token in order to use it for impersonation.
WindowsIdentity newId = new WindowsIdentity(dupeTokenHandle);
WindowsImpersonationContext impersonatedUser = newId.Impersonate();
// Check the identity.
Console.WriteLine("After impersonation: " + WindowsIdentity.GetCurrent().Name);
// Stop impersonating the user.
impersonatedUser.Undo();
// Check the identity.
Console.WriteLine("After Undo: " + WindowsIdentity.GetCurrent().Name);
// Free the tokens.
if (tokenHandle != IntPtr.Zero)
CloseHandle(tokenHandle);
if (dupeTokenHandle != IntPtr.Zero)
CloseHandle(dupeTokenHandle);
}
catch(Exception ex)
{
Console.WriteLine("Exception occurred. " + ex.Message);
}
}
}
I managed to make the processes start with this code:
ProcessHelper: http://pastie.org/private/dlkytj8rbigs8ixwtg
TokenImpersonationContext: http://pastie.org/private/nu3pvpghoea6pwwlvjuq
The service calls the StartAsUserFromService method, and the process calls the StartAsUserFromApplication method to start its successor.
I'm using LogonType.Batch in the LogonUser call because the process needs to talk to another WCF service and needs to authenticate. LogonType.Network and LogonType.NetworkClearText could be used, but caused permission issues in the Net.Tcp Port Sharing Service with the Worker user account.
This answer was helpful: Using Process.Start() to start a process as a different user from within a Windows Service
We have a scenario where we need our users to be able to launch SQLServer and authenticate into it using a different domain than they are currently logged into. So to clarify the way this is setup:
User arrives at the office and logs in to the corporate domain (lets call it LOCALDOMAIN for simplicity)
They wish to connect to our remote database on a different domain (lets call it REMOTEDOMAIN)
First they launch the VPN tool which establishes the VPN tunnel to REMOTEDOMAIN (this is all tested and works great)
But if they launch SSMS by default it will only allow Windows Auth via LOCALDOMAIN, the option to select REMOTEDOMAIN is not even available
What we discovered is that running this from the command line will work:
RUNAS /user:REMOTEDOMAIN\AUserName /netonly "C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\Ssms.exe
it will prompt with the message "Enter the password for REMOTEDOMAIN\AUserName:" and if you supply the correct password, SSMS will be launched and can connect to the remote dbs. However, when I try to do the same thing in C# with a nicer interface around it, I get "Logon failure: unknown user name or bad password", here is my code:
System.Security.SecureString password = new System.Security.SecureString();
foreach(char c in txtPassword.Text.ToCharArray()){
password.AppendChar(c);
}
System.Diagnostics.ProcessStartInfo procInfo = new System.Diagnostics.ProcessStartInfo();
procInfo.Arguments = "/netonly";
procInfo.FileName = #"C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\Ssms.exe"; ;
procInfo.Domain = "REMOTEDOMAIN";
procInfo.Verb = "runas";
procInfo.UserName = txtUsername.Text;
procInfo.Password = password;
procInfo.UseShellExecute = false;
System.Diagnostics.Process.Start(procInfo);
I tried the username with and without the domain pre-pended but neither works. Anyone ever tried to do something similar? thanks
You should remove the following lines:
// Not passing /netonly to SMSS, it was passed to RunAs originally.
procInfo.Arguments = "/netonly";
// Again, SMSS is not getting the verb, it's being run
procInfo.Verb = "runas";
Basically, you're passing the /netonly parameter to SMSS, whereas on the command line, you're running runas not SMSS. Same with the verb, you're not running runas.
The call to Start should succeed at that point, as you'll be pointing to the correct executable with the correct credentials.
I've done something that may be related. I login to one domain and try to get a directory listing of a shared folder on a different domain. To do this, I use LogonUser and Impersonate. The code looks like the following (sorry, I don't have an SQL server to try your exact scenario)...
public class Login : IDisposable
{
public Login(string userName, string domainName)
{
_userName = userName;
_domainName = domainName;
}
string _userName = null;
string _domainName = null;
IntPtr tokenHandle = new IntPtr(0);
IntPtr dupeTokenHandle = new IntPtr(0);
WindowsImpersonationContext impersonatedUser = null;
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
[DllImport("advapi32.dll", SetLastError = true, EntryPoint = "LogonUser")]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("advapi32.dll", SetLastError = true, EntryPoint = "LogonUser")]
public static extern bool LogonUserPrompt(String lpszUsername, String lpszDomain, IntPtr lpszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
public void AccessShare(string password)
{
tokenHandle = IntPtr.Zero;
bool returnValue = LogonUser(_userName, _domainName, password,
LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
ref tokenHandle);
if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(ret);
}
// Use the token handle returned by LogonUser.
WindowsIdentity newId = new WindowsIdentity(tokenHandle);
impersonatedUser = newId.Impersonate();
}
#region IDisposable Members
public void Dispose()
{
impersonatedUser.Undo();
// Free the tokens.
if (tokenHandle != IntPtr.Zero)
CloseHandle(tokenHandle);
}
#endregion
}
I've used this with Directory.GetDirectories(UNCPath) where the path leads to a machine on another domain and it works there. I have not yet tried it for implementing a "runas".
I call it like so...
using(var login = new Login("myname","mydomain))
{
login.AccessShare("mypassword");
// do stuff
}
Maybe you can adapt it to your problem. LMK
I tried all the various user impersonation code samples I could find. None of them worked.
Finally, I came up with the following code. It executes cmd.exe with the /C argument, which Carries out the command specified by string and then terminates . The command that I execute is runas /netonly ...
Caveats
Unfortunately, the password has to be typed manually. My next step is to investigate sending key stokes to the process. I tried redirecting standard input and writing to it, but it didn't work. I read somewhere on SO that most password prompts only accept input directly from the keyboard.
Also, when SSMS opens, the Connect to Server dialog will show your current domain\username, but it will authenticate using the one you gave to runas.
Finally, if your AD account is locked, you won't get an error until you try to connect to SQL Server. I neglected to copy down the error message that I received, but it did not mention the account was locked.
Code
public static void RunAsNetonly(string username, string domain, string exePath)
{
var psi = new ProcessStartInfo();
psi.FileName = "cmd.exe";
psi.Arguments = $"/C runas /netonly /user:{domain}\\{username} \"{exePath}\"";
psi.UseShellExecute = false;
var process = Process.Start(psi);
// not sure if this is required
process.WaitForExit();
}
// usage example
public static void RunSSMS()
{
RunAsNetonly("walter", "domain123", #"C:\Program Files (x86)\Microsoft SQL Server\140\Tools\Binn\ManagementStudio\ssms.exe");
}
We have a scenario where we need our users to be able to launch SQLServer and authenticate into it using a different domain than they are currently logged into. So to clarify the way this is setup:
User arrives at the office and logs in to the corporate domain (lets call it LOCALDOMAIN for simplicity)
They wish to connect to our remote database on a different domain (lets call it REMOTEDOMAIN)
First they launch the VPN tool which establishes the VPN tunnel to REMOTEDOMAIN (this is all tested and works great)
But if they launch SSMS by default it will only allow Windows Auth via LOCALDOMAIN, the option to select REMOTEDOMAIN is not even available
What we discovered is that running this from the command line will work:
RUNAS /user:REMOTEDOMAIN\AUserName /netonly "C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\Ssms.exe
it will prompt with the message "Enter the password for REMOTEDOMAIN\AUserName:" and if you supply the correct password, SSMS will be launched and can connect to the remote dbs. However, when I try to do the same thing in C# with a nicer interface around it, I get "Logon failure: unknown user name or bad password", here is my code:
System.Security.SecureString password = new System.Security.SecureString();
foreach(char c in txtPassword.Text.ToCharArray()){
password.AppendChar(c);
}
System.Diagnostics.ProcessStartInfo procInfo = new System.Diagnostics.ProcessStartInfo();
procInfo.Arguments = "/netonly";
procInfo.FileName = #"C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\Ssms.exe"; ;
procInfo.Domain = "REMOTEDOMAIN";
procInfo.Verb = "runas";
procInfo.UserName = txtUsername.Text;
procInfo.Password = password;
procInfo.UseShellExecute = false;
System.Diagnostics.Process.Start(procInfo);
I tried the username with and without the domain pre-pended but neither works. Anyone ever tried to do something similar? thanks
You should remove the following lines:
// Not passing /netonly to SMSS, it was passed to RunAs originally.
procInfo.Arguments = "/netonly";
// Again, SMSS is not getting the verb, it's being run
procInfo.Verb = "runas";
Basically, you're passing the /netonly parameter to SMSS, whereas on the command line, you're running runas not SMSS. Same with the verb, you're not running runas.
The call to Start should succeed at that point, as you'll be pointing to the correct executable with the correct credentials.
I've done something that may be related. I login to one domain and try to get a directory listing of a shared folder on a different domain. To do this, I use LogonUser and Impersonate. The code looks like the following (sorry, I don't have an SQL server to try your exact scenario)...
public class Login : IDisposable
{
public Login(string userName, string domainName)
{
_userName = userName;
_domainName = domainName;
}
string _userName = null;
string _domainName = null;
IntPtr tokenHandle = new IntPtr(0);
IntPtr dupeTokenHandle = new IntPtr(0);
WindowsImpersonationContext impersonatedUser = null;
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
[DllImport("advapi32.dll", SetLastError = true, EntryPoint = "LogonUser")]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("advapi32.dll", SetLastError = true, EntryPoint = "LogonUser")]
public static extern bool LogonUserPrompt(String lpszUsername, String lpszDomain, IntPtr lpszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
public void AccessShare(string password)
{
tokenHandle = IntPtr.Zero;
bool returnValue = LogonUser(_userName, _domainName, password,
LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
ref tokenHandle);
if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(ret);
}
// Use the token handle returned by LogonUser.
WindowsIdentity newId = new WindowsIdentity(tokenHandle);
impersonatedUser = newId.Impersonate();
}
#region IDisposable Members
public void Dispose()
{
impersonatedUser.Undo();
// Free the tokens.
if (tokenHandle != IntPtr.Zero)
CloseHandle(tokenHandle);
}
#endregion
}
I've used this with Directory.GetDirectories(UNCPath) where the path leads to a machine on another domain and it works there. I have not yet tried it for implementing a "runas".
I call it like so...
using(var login = new Login("myname","mydomain))
{
login.AccessShare("mypassword");
// do stuff
}
Maybe you can adapt it to your problem. LMK
I tried all the various user impersonation code samples I could find. None of them worked.
Finally, I came up with the following code. It executes cmd.exe with the /C argument, which Carries out the command specified by string and then terminates . The command that I execute is runas /netonly ...
Caveats
Unfortunately, the password has to be typed manually. My next step is to investigate sending key stokes to the process. I tried redirecting standard input and writing to it, but it didn't work. I read somewhere on SO that most password prompts only accept input directly from the keyboard.
Also, when SSMS opens, the Connect to Server dialog will show your current domain\username, but it will authenticate using the one you gave to runas.
Finally, if your AD account is locked, you won't get an error until you try to connect to SQL Server. I neglected to copy down the error message that I received, but it did not mention the account was locked.
Code
public static void RunAsNetonly(string username, string domain, string exePath)
{
var psi = new ProcessStartInfo();
psi.FileName = "cmd.exe";
psi.Arguments = $"/C runas /netonly /user:{domain}\\{username} \"{exePath}\"";
psi.UseShellExecute = false;
var process = Process.Start(psi);
// not sure if this is required
process.WaitForExit();
}
// usage example
public static void RunSSMS()
{
RunAsNetonly("walter", "domain123", #"C:\Program Files (x86)\Microsoft SQL Server\140\Tools\Binn\ManagementStudio\ssms.exe");
}