I'm trying to attach a console to a Service if im running it from the debugger.
I've read up a couple of "working" solutions, but those seem not to really work. Here is he Code I use:
public static void RunService(Func<ServiceBase> factory)
{
if (Debugger.IsAttached)
{
Utils.AttachConsole();
Console.Write($"Starting service ");
var instance = factory();
Console.WriteLine(instance.GetType().Name);
//Invoke start Method
Console.WriteLine("Press [ENTER] to exit");
Console.ReadLine();
//Stop service
}
else
{
ServiceBase.Run(factory());
}
}
Alloc Console is:
public static void AttachConsole()
{
var ret = NativeMethods.AllocConsole();
IntPtr currentStdout = NativeMethods.GetStdHandle(NativeMethods.STD_OUTPUT_HANDLE);
NativeMethods.SetStdHandle(NativeMethods.STD_OUTPUT_HANDLE, new IntPtr(7));
TextWriter writer = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true };
Console.SetOut(writer);
}
and the Interop includes:
internal static class NativeMethods
{
internal const uint STD_OUTPUT_HANDLE = 0xFFFFFFF5;
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool AllocConsole();
[DllImport("kernel32.dll")]
internal static extern IntPtr GetStdHandle(uint nStdHandle);
[DllImport("kernel32.dll")]
internal static extern void SetStdHandle(uint nStdHandle, IntPtr handle);
}
What happens is, a console is created and attached, but there is no output. Can't be to difficult, but I'm to dumb to see it :(
EDIT:
The Issue is Visual Studio, it is not the code "itself". Without VS, I can get a console and receive the expected output there. There is some kind of redirection in VS, which I am looking to overcome here.
EDIT just for Hans - here is the "full code"
static void Main(string[] args)
{
ServiceLauncher.RunService(() => new FactoryService();
}
The project is set to application type windows.
I was able to reproduce your issue and get it working on my machine. Some of your code looks like it comes from the accepted answer at No console output when using AllocConsole and target architecture x86. If you read the comment thread under that answer, you will see that new IntPtr(7) does not work as of Windows 7/ Server 2012. The "new magic number" for Windows 7 also did not work for me. To solve this, I started down the path of porting the given c++ call from the comments into c#, which required some signature changes for the PInvokes (which were all copied and pasted from PInvoke.net, so they should be fine). The changes that I made are almost exclusively in the PInvoke code. Here is a full working code set:
Program.cs (unchanged):
static void Main()
{
ServiceLauncher.RunService(() => new Service1());
}
ServiceLauncher.cs (unchanged):
public static void RunService(Func<ServiceBase> factory)
{
if (Debugger.IsAttached)
{
Utils.AttachConsole();
Console.Write($"Starting service ");
var instance = factory();
Console.WriteLine(instance.GetType().Name);
//Invoke start Method
Console.WriteLine("Press [ENTER] to exit");
Console.ReadLine();
//Stop service
}
else
{
ServiceBase.Run(factory());
}
}
Utils.cs (1 change as documemented in comments):
public static void AttachConsole()
{
var ret = NativeMethods.AllocConsole();
IntPtr currentStdout = NativeMethods.GetStdHandle(NativeMethods.STD_OUTPUT_HANDLE);
// IntPtr(7) was a dangerous assumption that doesn't work on current versions of Windows...
//NativeMethods.SetStdHandle(NativeMethods.STD_OUTPUT_HANDLE, new IntPtr(7));
// Instead, get the defaultStdOut using PInvoke
SafeFileHandle defaultStdOut = NativeMethods.CreateFile("CONOUT$", EFileAccess.GenericRead | EFileAccess.GenericWrite, EFileShare.Write, IntPtr.Zero, ECreationDisposition.OpenExisting, 0, IntPtr.Zero);
NativeMethods.SetStdHandle(NativeMethods.STD_OUTPUT_HANDLE, defaultStdOut.DangerousGetHandle()); // also seems dangerous... there may be an alternate signature for SetStdHandle that takes SafeFileHandle.
TextWriter writer = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true };
Console.SetOut(writer);
}
NativeMethods.cs (almost completely different - links and explanations given in comments). Enums included in this file (outside of the class scope) but can be moved to different files at your discretion:
internal static class NativeMethods
{
// 0xFFFFFFF5 is not consistent with what I found...
//internal const uint STD_OUTPUT_HANDLE = 0xFFFFFFF5;
// https://www.pinvoke.net/default.aspx/kernel32.getstdhandle
internal const int STD_OUTPUT_HANDLE = -11;
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool AllocConsole();
// method signature changed per https://www.pinvoke.net/default.aspx/kernel32.getstdhandle
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GetStdHandle(int nStdHandle);
// method signature changed per https://www.pinvoke.net/default.aspx/kernel32.setstdhandle
[DllImport("kernel32.dll")]
internal static extern bool SetStdHandle(int nStdHandle, IntPtr hHandle);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern SafeFileHandle CreateFile(
string lpFileName,
EFileAccess dwDesiredAccess,
EFileShare dwShareMode,
IntPtr lpSecurityAttributes,
ECreationDisposition dwCreationDisposition,
EFileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);
}
// ENUMS FROM http://www.pinvoke.net/default.aspx/kernel32/CreateFile.html
[Flags]
public enum EFileAccess : uint
{
//
// Standart Section
//
AccessSystemSecurity = 0x1000000, // AccessSystemAcl access type
MaximumAllowed = 0x2000000, // MaximumAllowed access type
Delete = 0x10000,
ReadControl = 0x20000,
WriteDAC = 0x40000,
WriteOwner = 0x80000,
Synchronize = 0x100000,
StandardRightsRequired = 0xF0000,
StandardRightsRead = ReadControl,
StandardRightsWrite = ReadControl,
StandardRightsExecute = ReadControl,
StandardRightsAll = 0x1F0000,
SpecificRightsAll = 0xFFFF,
FILE_READ_DATA = 0x0001, // file & pipe
FILE_LIST_DIRECTORY = 0x0001, // directory
FILE_WRITE_DATA = 0x0002, // file & pipe
FILE_ADD_FILE = 0x0002, // directory
FILE_APPEND_DATA = 0x0004, // file
FILE_ADD_SUBDIRECTORY = 0x0004, // directory
FILE_CREATE_PIPE_INSTANCE = 0x0004, // named pipe
FILE_READ_EA = 0x0008, // file & directory
FILE_WRITE_EA = 0x0010, // file & directory
FILE_EXECUTE = 0x0020, // file
FILE_TRAVERSE = 0x0020, // directory
FILE_DELETE_CHILD = 0x0040, // directory
FILE_READ_ATTRIBUTES = 0x0080, // all
FILE_WRITE_ATTRIBUTES = 0x0100, // all
//
// Generic Section
//
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000,
SPECIFIC_RIGHTS_ALL = 0x00FFFF,
FILE_ALL_ACCESS =
StandardRightsRequired |
Synchronize |
0x1FF,
FILE_GENERIC_READ =
StandardRightsRead |
FILE_READ_DATA |
FILE_READ_ATTRIBUTES |
FILE_READ_EA |
Synchronize,
FILE_GENERIC_WRITE =
StandardRightsWrite |
FILE_WRITE_DATA |
FILE_WRITE_ATTRIBUTES |
FILE_WRITE_EA |
FILE_APPEND_DATA |
Synchronize,
FILE_GENERIC_EXECUTE =
StandardRightsExecute |
FILE_READ_ATTRIBUTES |
FILE_EXECUTE |
Synchronize
}
[Flags]
public enum EFileShare : uint
{
/// <summary>
///
/// </summary>
None = 0x00000000,
/// <summary>
/// Enables subsequent open operations on an object to request read access.
/// Otherwise, other processes cannot open the object if they request read access.
/// If this flag is not specified, but the object has been opened for read access, the function fails.
/// </summary>
Read = 0x00000001,
/// <summary>
/// Enables subsequent open operations on an object to request write access.
/// Otherwise, other processes cannot open the object if they request write access.
/// If this flag is not specified, but the object has been opened for write access, the function fails.
/// </summary>
Write = 0x00000002,
/// <summary>
/// Enables subsequent open operations on an object to request delete access.
/// Otherwise, other processes cannot open the object if they request delete access.
/// If this flag is not specified, but the object has been opened for delete access, the function fails.
/// </summary>
Delete = 0x00000004
}
public enum ECreationDisposition : uint
{
/// <summary>
/// Creates a new file. The function fails if a specified file exists.
/// </summary>
New = 1,
/// <summary>
/// Creates a new file, always.
/// If a file exists, the function overwrites the file, clears the existing attributes, combines the specified file attributes,
/// and flags with FILE_ATTRIBUTE_ARCHIVE, but does not set the security descriptor that the SECURITY_ATTRIBUTES structure specifies.
/// </summary>
CreateAlways = 2,
/// <summary>
/// Opens a file. The function fails if the file does not exist.
/// </summary>
OpenExisting = 3,
/// <summary>
/// Opens a file, always.
/// If a file does not exist, the function creates a file as if dwCreationDisposition is CREATE_NEW.
/// </summary>
OpenAlways = 4,
/// <summary>
/// Opens a file and truncates it so that its size is 0 (zero) bytes. The function fails if the file does not exist.
/// The calling process must open the file with the GENERIC_WRITE access right.
/// </summary>
TruncateExisting = 5
}
[Flags]
public enum EFileAttributes : uint
{
Readonly = 0x00000001,
Hidden = 0x00000002,
System = 0x00000004,
Directory = 0x00000010,
Archive = 0x00000020,
Device = 0x00000040,
Normal = 0x00000080,
Temporary = 0x00000100,
SparseFile = 0x00000200,
ReparsePoint = 0x00000400,
Compressed = 0x00000800,
Offline = 0x00001000,
NotContentIndexed = 0x00002000,
Encrypted = 0x00004000,
Write_Through = 0x80000000,
Overlapped = 0x40000000,
NoBuffering = 0x20000000,
RandomAccess = 0x10000000,
SequentialScan = 0x08000000,
DeleteOnClose = 0x04000000,
BackupSemantics = 0x02000000,
PosixSemantics = 0x01000000,
OpenReparsePoint = 0x00200000,
OpenNoRecall = 0x00100000,
FirstPipeInstance = 0x00080000
}
I do this for every windows service that I develop. But usually I use a winform and even in some cases named pipes for being able to do more fancy stuff than watching the output in console.
That being said you don't have to do anything fancy to get console output for windows services.
1) Create a windows service project
2) change your Project output to Console application.
3) change the "Service1" class to this.
using System.ServiceProcess;
namespace WindowsService1
{
public partial class Service1 : ServiceBase
{
readonly Runner _runner = new Runner();
static void Main(string[] args)
{
var service = new Service1();
if (Debugger.IsAttached)
{
service.OnStart(args);
Console.WriteLine("Find the any key!");
Console.Read();
service.OnStop();
}
else
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
service
};
ServiceBase.Run(ServicesToRun);
}
}
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
_runner.Run();
}
protected override void OnStop()
{
_runner.Stop();
}
}
}
Then in the Runner class you do what ever you are getting from your factory method.
I've always found it easier to use Topshelf to debug service applications (http://topshelf-project.com/)
Related
Computer A is on Domain A
Computer B is on Domain B
Domain A and Domain B have a trust allowing Accounts to connect to the other domain's computers
Account A is Admin on Computer A and has Backup&Restore Privileges
Account B is Admin on Computer B and has Backup&Restore Privileges
Account A IS NOT Admin on Computer B and does not have Backup&Restore Privileges
Account B IS NOT Admin on Computer A and does not have Backup&Restore Privileges
A person owns both Account A and Account B.
They want to copy a file from Computer A to Computer B.
They wish to copy this file from and into a folder they do not have permissions for. Therefore, they must both enable Backup&Restore Privileges on their Access Token they got from LogonUser
It is not allowed to change the DACL to grant these permissions on any folders or files and instead both Account A and Account B must enable B&R privileges which is recognized by the Computer, only then will they be able to copy the file without Access Denied.
The problem is I have tried using NETWORK, INTERACTIVE, and NEW_CREDENTIALS Access Tokens and enabling privileges on them, but only NETWORK contains the privileges and they are already enabled by default. Then when I try to WindowsIdentity.Impersonate the NETWORK token and then call "CreateFile" to open the file with privileges, it fails and returns an invalid file handle of -1. I can use INTERACTIVE to read an unrestricted file but it didn't have Access Token Privileges needed ( SeBackupPrivilege & SeRestorePrivilege ) when it was returned from LogonUser. I assume once Impersonate happens it generates a Token that "might" have those privileges, but I assume that's based on the machine the code is running on.
Is there a way to Impersonate Access Token -> Enable Access Token B&R Privileges on the remote computers they would have normally when at that machine running as administrator which could be enabled.
OR
Is there a way to use the NETWORK Token with Impersonation to successfully copy the file from Computer on Domain A to Computer on Domain B. If I run the program as Account B who isn't admin trying to impersonate Account A with network credential, it appears to not work on impersonation
Below is demo code in a Console Application that emulates the situation. You must change parts of it to test it accordingly:
You must create the file paths and the file to be read.
You must edit permissions on "DENYTHEIMPERSONATINGUSERHERE" folder so the impersonating user account is denied and must use privileges.
You must enter an actual account credentials to get an AccessToken.
class Program
{
static void Main(string[] args)
{
//Credentials of Account A
string usernameA = "AccountA";
string DomainA = "DomainA.com";
SecureString passwordA = new SecureString();
passwordA.AppendChar('P');
passwordA.AppendChar('W');
passwordA.AppendChar('D');
passwordA.AppendChar('A');
IntPtr AccessToken = IntPtr.Zero;
//Getting Network Access Token. (Network is an Impersonation Token, most other types are returned as Primary Tokens)
AccessTokenHelper.LOGON32_LOGONERROR lgnCode = AccessTokenHelper.GetAccessToken(DomainA, usernameA, passwordA, AccessTokenHelper.LOGON32_LOGONTYPE.LOGON32_LOGON_NETWORK, AccessTokenHelper.LOGON32_LOGONPROVIDER.LOGON32_PROVIDER_WINNT50, out AccessToken);
/*
//Getting INTERACTIVE Access Token. (returns Primary Token)
//Impersonation will work but won't have enabled privileges so will error when trying to write file.
AccessTokenHelper.LOGON32_LOGONERROR lgnCode = AccessTokenHelper.GetAccessToken(DomainA, usernameA, passwordA, AccessTokenHelper.LOGON32_LOGONTYPE.LOGON32_LOGON_INTERACTIVE, AccessTokenHelper.LOGON32_LOGONPROVIDER.LOGON32_PROVIDER_WINNT50, out AccessToken);
*/
if(AccessToken == IntPtr.Zero || AccessToken.ToInt32() == -1)
{
//Not valid creds
System.Diagnostics.Debug.WriteLine(lgnCode.ToString());
return;
}
//Enable Token Security Privileges
AccessTokenHelper.ElevateSecurityPrivileges(AccessToken);
//List Enabled Token Privileges
List<string> tokenEnabledPrivileges = AccessTokenHelper.ListEnabledPrivileges(AccessToken);
foreach(string s in tokenEnabledPrivileges)
{
System.Diagnostics.Debug.WriteLine(s);
}
string sourceFile = #"C:\Temp\Test1\TestData.txt";
string destFile = #"C:\Temp\Test1\DENYTHEIMPERSONATINGUSERHERE\PrivCopy.txt";
bool bCopied = PrivilegedEnterpriseCopyFile(sourceFile, destFile, AccessToken, AccessToken);
System.Diagnostics.Debug.WriteLine("DID THE COPY WORK? " + bCopied.ToString().ToUpper());
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
OPENFILE_SHAREACCESS dwShareMode, //If you are reading folders you better have OPENFILE_SHAREACCESS.FILE_SHARE_READ
IntPtr SecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
private enum OPENFILE_SHAREACCESS : uint
{
NO_SHARING = 0x00000000,
FILE_SHARE_READ = 0x00000001,
FILE_SHARE_WRITE = 0x00000002,
FILE_SHARE_DELETE = 0x00000004
};
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool GetFileSizeEx(IntPtr hFile, out long lpFileSize);
/// <summary>
/// Goal: Enterprise Level Copy.... can copy file data between remote machines between domains.
/// </summary>
public static bool PrivilegedEnterpriseCopyFile(string sourceAbsoluteFileName, string destAbsoluteFileName, IntPtr sourceAccessToken, IntPtr destAccessToken, bool bOverwrite = false)
{
const uint GENERIC_READ = 0x80000000;
const uint GENERIC_WRITE = 0x40000000; //If you are writing to a file, best allow both READ and WRITE because Windows Performance suffers without.
const uint CREATE_ALWAYS = 2; //Create and overwrite if necesarry
const uint OPEN_EXISTING = 3; //Open only if exists
const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; //This makes it take enabled Backup&Restore privileges into account
const uint FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000; //This helps speed up reading.
const uint FILE_FLAG_WRITE_THROUGH = 0x80000000; //This helps speed up writing!
//We pick a value that is the largest multiple of 4096 that is still smaller than the large object heap threshold (85K).
// The CopyTo/CopyToAsync buffer is short-lived and is likely to be collected at Gen0, and it offers a significant
// improvement in Copy performance.
const int DefaultCopyBufferSize = 81920; //80kb
bool bSuccess = false;
//Get File Ptr Handles. DO NOT ALLOW DELETE ON SOURCE OR DEST WHILE THIS IS HAPPENING.
//Get the file pointer by using an access token with access.
IntPtr pSource = IntPtr.Zero;
IntPtr pDest = IntPtr.Zero;
//As source user, connect for optimized READING
using (WindowsImpersonationContext impersonatedUser = WindowsIdentity.Impersonate(sourceAccessToken))
{
pSource = CreateFile(sourceAbsoluteFileName, GENERIC_READ, OPENFILE_SHAREACCESS.FILE_SHARE_READ,
IntPtr.Zero, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN, IntPtr.Zero);
}
if (pSource == IntPtr.Zero || pSource.ToInt32() == -1)
{
//Failed to get Source, return false
return bSuccess;
}
//Get Dest Path
string DestPath = Path.GetDirectoryName(destAbsoluteFileName);
string DestFileName = Path.GetFileName(destAbsoluteFileName);
//As dest user
using (WindowsImpersonationContext impersonatedUser = WindowsIdentity.Impersonate(destAccessToken))
{
try
{
bool bProceed = true;
if (!bOverwrite)
{
//We don't want to overwrite existing file, ensure it doesn't exist.
List<string> files = Directory.EnumerateFiles(DestPath).ToList();
if (files.Any(s => s.Equals(DestFileName, StringComparison.OrdinalIgnoreCase)))
{
//File exists, do not proceed
bProceed = false;
}
}
//Do we proceed?
if (bProceed)
{
//Create/Overwrite existing File
pDest = CreateFile(destAbsoluteFileName, GENERIC_READ | GENERIC_WRITE, OPENFILE_SHAREACCESS.NO_SHARING,
IntPtr.Zero, CREATE_ALWAYS,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_WRITE_THROUGH, IntPtr.Zero);
}
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
//If we successfully have both File Handles, we can proceed with Reading from source and writing to Dest.
//If valid file pointers!!!
if (pSource != IntPtr.Zero && pSource.ToInt32() != -1 &&
pDest != IntPtr.Zero && pDest.ToInt32() != -1)
{
//Put Handle into more supported SafeFileHandle Ptr.
SafeFileHandle safeSourcePtr = new SafeFileHandle(pSource, false); //We will close handle manually
SafeFileHandle safeDestPtr = new SafeFileHandle(pDest, false); //We will close handle manually
try
{
using (FileStream fsSource = new FileStream(safeSourcePtr, FileAccess.Read, DefaultCopyBufferSize))
{
using (FileStream fsDest = new FileStream(safeDestPtr, FileAccess.ReadWrite, DefaultCopyBufferSize))
{
//Here we read X bytes up to limit from fsSource and write to fsDest, until no more bytes!
// Read the source file into a byte array.
byte[] buffer = new byte[DefaultCopyBufferSize];
int bytesRead = -1;
while ((bytesRead = fsSource.Read(buffer, 0, buffer.Length)) > 0)
{
fsDest.Write(buffer, 0, bytesRead);
}
//Force data to be flushed to harddisk.
fsDest.Flush(true);
}
}
}
catch { }
//Compare File Size to know if we successfully wrote bytes to destination!
//We'll assume it'd error out if it didn't.
GetFileSizeEx(pSource, out long sourceSize);
GetFileSizeEx(pDest, out long destSize);
if (sourceSize == destSize)
{
//consider it a success
bSuccess = true;
}
}
if (pSource != IntPtr.Zero && pSource.ToInt32() != -1)
{
//Close file handle manually
CloseHandle(pSource);
}
if (pDest != IntPtr.Zero && pDest.ToInt32() != -1)
{
//Close file handle manually
CloseHandle(pDest);
}
return bSuccess;
}
}
public class AccessTokenHelper
{
public enum LOGON32_LOGONTYPE : int
{
LOGON32_LOGON_INTERACTIVE = 2,
LOGON32_LOGON_NETWORK = 3,
LOGON32_LOGON_BATCH = 4,
LOGON32_LOGON_SERVICE = 5,
LOGON32_LOGON_UNLOCK = 7,
LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
LOGON32_LOGON_NEW_CREDENTIALS = 9,
};
public enum LOGON32_LOGONPROVIDER : int
{
LOGON32_PROVIDER_DEFAULT = 0,
LOGON32_PROVIDER_WINNT40 = 2,
LOGON32_PROVIDER_WINNT50 = 3,
};
public enum LOGON32_LOGONERROR : int
{
ERROR_SUCCESS = 0,
ERROR_NO_LOGON_SERVERS = 1311,
ERROR_INVALID_ACCOUNT_NAME = 1315,
ERROR_LOGON_FAILURE = 1326,
ERROR_ACCOUNT_RESTRICTION = 1327,
ERROR_INVALID_LOGON_HOURS = 1328,
ERROR_INVALID_WORKSTATION_LOGONDENIED = 1329,
ERROR_PASSWORD_EXPIRED = 1330,
ERROR_ACCOUNT_DISABLED = 1331,
ERROR_INVALID_LOGON_TYPE = 1367,
ERROR_LOGON_NOT_GRANTED = 1380,
ERROR_NETLOGON_NOT_STARTED = 1792,
ERROR_ACCOUNT_EXPIRED = 1793,
ERROR_PASSWORD_MUST_CHANGE = 1907,
ERROR_ACCOUNT_LOCKED_OUT = 1909,
};
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private extern static bool LogonUser(string username, string domain, IntPtr password, LOGON32_LOGONTYPE logonType, LOGON32_LOGONPROVIDER logonProvider, out IntPtr token);
/// <summary>
/// Attempts to create Access token from information given.
/// </summary>
public static LOGON32_LOGONERROR GetAccessToken(string domain, string username, SecureString securepassword, LOGON32_LOGONTYPE eLOGONTYPE, LOGON32_LOGONPROVIDER eLOGONPROVIDER, out IntPtr token)
{
token = IntPtr.Zero;
// Marshal the SecureString to unmanaged memory. I hate doing this but it's currently most secure way offered by Microsoft to get Access Token from Credentials.
IntPtr passwordPtr = Marshal.SecureStringToGlobalAllocUnicode(securepassword);
bool bSuccess = LogonUser(username,
domain,
passwordPtr,
eLOGONTYPE,
eLOGONPROVIDER,
out token);
//return the error code, useful if not successful.
int errResult = Marshal.GetLastWin32Error();
// Zero-out and free the unmanaged string reference.
Marshal.ZeroFreeGlobalAllocUnicode(passwordPtr);
return (LOGON32_LOGONERROR)errResult;
}
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool LookupPrivilegeName(string lpSystemName, IntPtr lpLuid, StringBuilder lpName, ref int cchName);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool LookupPrivilegeDisplayName(string systemName, string privilegeName, StringBuilder displayName, ref int cchDisplayName, out uint languageId);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out long lpLuid);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool AdjustTokenPrivileges(
IntPtr TokenHandle,
[MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges,
ref TOKEN_PRIVILEGES NewState,
uint prevStateBuffer,
IntPtr prevStateNA,
IntPtr prevBufferNA);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool GetTokenInformation(
IntPtr hToken,
uint TokenInformationClass,
IntPtr TokenInformation,
int TokenInformationLength,
out int ReturnLength);
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public uint LowPart;
public uint HighPart;
};
[StructLayout(LayoutKind.Sequential)]
public struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public uint Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_PRIVILEGES
{
public int PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public LUID_AND_ATTRIBUTES[] Privileges;
};
[Flags]
public enum SE_PRIVILEGE_STATE : uint
{
SE_PRIVILEGE_DISABLED = 0x00,
SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x01,
SE_PRIVILEGE_ENABLED = 0x02,
SE_PRIVILEGE_REMOVED = 0x04,
SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000
};
/// <summary>
/// Will elevate Backup and Restore Privileges of Access Token (be it Primary Type or Impersonation Type) if Access Token has Privilege
/// 1) SeBackupPrivilege -- this grants read access regardless of DACL entries.
/// 2) SeRestorePrivilege -- this grants write access regardless of DACL entries.
/// 3) SeSecurityPrivilege -- this grants access to SACL for audits/security log.
/// </summary>
public static void ElevateSecurityPrivileges(IntPtr hToken, bool bEnabled = true)
{
SE_PRIVILEGE_STATE privSTATE = bEnabled ? SE_PRIVILEGE_STATE.SE_PRIVILEGE_ENABLED : SE_PRIVILEGE_STATE.SE_PRIVILEGE_DISABLED;
List<string> SecurityPrivNames = new List<string>() { "SeBackupPrivilege", "SeRestorePrivilege", "SeSecurityPrivilege" };
if (hToken != IntPtr.Zero)
{
AdjustAccessTokenPrivileges(hToken, SecurityPrivNames, privSTATE);
}
}
public static void AdjustAccessTokenPrivileges(IntPtr TokenHandle, List<string> PrivilegeNames, SE_PRIVILEGE_STATE eNewPrivilegeState, string remoteMachineName = null)
{
if (TokenHandle != IntPtr.Zero && PrivilegeNames != null && PrivilegeNames.Count > 0)
{
DataTable privDT = GetAccessTokenPrivilegesAsDataTable(TokenHandle, remoteMachineName);
if (privDT != null && privDT.Rows.Count > 0)
{
//If we have privileges, try to set state!
foreach (string privName in PrivilegeNames)
{
DataRow row = privDT.Select(string.Format("[{0}]='{1}'", privDT.Columns[1].ColumnName, privName)).FirstOrDefault();
if (row != null)
{
UpdateExistingTokenPrivilege(TokenHandle, row.Field<LUID>(0), eNewPrivilegeState);
}
}
}
}
}
private static bool UpdateExistingTokenPrivilege(IntPtr tokenHandle, LUID privLuid, SE_PRIVILEGE_STATE eNewPrivilegeState)
{
//Create our updated tokenPriv to send what privs we want updated.
TOKEN_PRIVILEGES tokenPrivs = new TOKEN_PRIVILEGES();
tokenPrivs.PrivilegeCount = 1;
tokenPrivs.Privileges = new LUID_AND_ATTRIBUTES[] { new LUID_AND_ATTRIBUTES() { Luid = privLuid, Attributes = (uint)eNewPrivilegeState } };
//Adjust Token Privilege!
bool bSuccess = AdjustTokenPrivileges(tokenHandle, false, ref tokenPrivs, 0, IntPtr.Zero, IntPtr.Zero);
//Return result of trying to adjust token privilege.
return bSuccess;
}
public static DataTable GetAccessTokenPrivilegesAsDataTable(IntPtr TokenHandle, string remoteMachineName = null)
{
TOKEN_PRIVILEGES tokenPrivData = GetAccessTokenPrivileges(TokenHandle);
DataTable privDT = new DataTable();
privDT.Columns.Add("PrivilegeLUID", typeof(LUID));
privDT.Columns.Add("PrivilegeName");
privDT.Columns.Add("PrivilegeState", typeof(SE_PRIVILEGE_STATE));
foreach (LUID_AND_ATTRIBUTES privData in tokenPrivData.Privileges)
{
string PrivilegeName = LookupPrivilegeName(privData.Luid, remoteMachineName);
if (!string.IsNullOrEmpty(PrivilegeName))
{
DataRow row = privDT.NewRow();
row[0] = privData.Luid;
row[1] = PrivilegeName;
row[2] = (SE_PRIVILEGE_STATE)privData.Attributes;
//Add Row
privDT.Rows.Add(row);
}
}
return privDT;
}
private static TOKEN_PRIVILEGES GetAccessTokenPrivileges(IntPtr TokenHandle)
{
uint TOKEN_INFORMATION_CLASS_TokenPrivileges = 3;
if (TokenHandle != IntPtr.Zero)
{
int nBufferSize = 0;
bool TokenInfoResult;
//First call is to get buffer size!
TokenInfoResult = GetTokenInformation(TokenHandle, TOKEN_INFORMATION_CLASS_TokenPrivileges, IntPtr.Zero, nBufferSize, out nBufferSize);
//Allocate Token Info correctly.
IntPtr pTokenInfo = Marshal.AllocHGlobal(nBufferSize);
//Get our Token Info Data
TokenInfoResult = GetTokenInformation(TokenHandle, TOKEN_INFORMATION_CLASS_TokenPrivileges, pTokenInfo, nBufferSize, out nBufferSize);
if (TokenInfoResult)
{
TOKEN_PRIVILEGES returnedPrivilegeSet = (TOKEN_PRIVILEGES)Marshal.PtrToStructure(pTokenInfo, typeof(TOKEN_PRIVILEGES));
int PrivilegeCount = returnedPrivilegeSet.PrivilegeCount;
//lets create the array we should have had returned in the first place
LUID_AND_ATTRIBUTES[] AllPrivs = new LUID_AND_ATTRIBUTES[PrivilegeCount]; //initialize an array to the right size, including 0!
if (PrivilegeCount > 0)
{
LUID_AND_ATTRIBUTES currentPriv = new LUID_AND_ATTRIBUTES();
//pPrivileges will hold our new location to read from by taking the last pointer plus the size of the last structure read
IntPtr pPrivilege = new IntPtr(pTokenInfo.ToInt32() + sizeof(int)); //pointer math, we point to the first element of Privileges array in the struct.
currentPriv = (LUID_AND_ATTRIBUTES)Marshal.PtrToStructure(pPrivilege, typeof(LUID_AND_ATTRIBUTES)); //Get Privilege from pointer
AllPrivs[0] = currentPriv; //We'll add the first element to our array.
//After getting our first structure we can loop through the rest since they will all be the same
for (int i = 1; i < PrivilegeCount; ++i)
{
pPrivilege = new IntPtr(pPrivilege.ToInt32() + Marshal.SizeOf(currentPriv)); //This will point to the next Privilege element in the array
currentPriv = (LUID_AND_ATTRIBUTES)Marshal.PtrToStructure(pPrivilege, typeof(LUID_AND_ATTRIBUTES)); //Get Privilege from pointer
AllPrivs[i] = currentPriv; //Add element to the array
}
}
//Create our complete struct of TOKEN_PRIVILEGES
TOKEN_PRIVILEGES completePrivilegeSet = new TOKEN_PRIVILEGES();
completePrivilegeSet.PrivilegeCount = PrivilegeCount;
completePrivilegeSet.Privileges = AllPrivs;
//We can get release all the pointers now, we got what we wanted!
Marshal.FreeHGlobal(pTokenInfo); //Free up the reserved space in unmanaged memory (Should be done any time AllocHGlobal is used)
//Return our completePrivilegeSet!
return completePrivilegeSet;
}
}
return new TOKEN_PRIVILEGES() { PrivilegeCount = 0, Privileges = new LUID_AND_ATTRIBUTES[] { } };
}
private static string LookupPrivilegeName(LUID privLuid, string remoteMachineName = null)
{
string PrivilegeName = null;
StringBuilder sb = new StringBuilder();
int cchName = 0; //Holds the length of structure we will be receiving LookupPrivilagename
IntPtr ipLuid = Marshal.AllocHGlobal(Marshal.SizeOf(privLuid)); //Allocate a block of memory large enough to hold the structure
Marshal.StructureToPtr(privLuid, ipLuid, true); //Write the structure into the reserved space in unmanaged memory
LookupPrivilegeName(remoteMachineName, ipLuid, null, ref cchName); // call once to get the name length we will be receiving
sb.Capacity = cchName; //Our string builder is buffered for the name!
if (LookupPrivilegeName(remoteMachineName, ipLuid, sb, ref cchName))
{
// Successfully retrieved name!
PrivilegeName = sb.ToString();
}
Marshal.FreeHGlobal(ipLuid); //Free up the reserved space in unmanaged memory
return PrivilegeName;
}
public static List<string> ListEnabledPrivileges(IntPtr TokenHandle, string remoteMachineName = null)
{
List<string> enabledPrivs = null;
DataTable dt = GetAccessTokenPrivilegesAsDataTable(TokenHandle, remoteMachineName);
if (dt != null && dt.Rows.Count > 0)
{
uint nEnabled = (uint)SE_PRIVILEGE_STATE.SE_PRIVILEGE_ENABLED;
uint nEnabledAndDefault = (uint)(SE_PRIVILEGE_STATE.SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_STATE.SE_PRIVILEGE_ENABLED);
string query = string.Format("[{0}] IN ( {1}, {2} )"
, dt.Columns[2].ColumnName
, nEnabled
, nEnabledAndDefault);
IEnumerable<DataRow> rows = dt.Select(query);
if (rows != null && rows.Count() > 0)
{
enabledPrivs = dt.Select(query).Select(r => r.Field<string>(1)).ToList();
}
}
return enabledPrivs;
}
}
So after much research it turns out this is locked down in Windows. Unless the current process token has SeTcbPrivilege (Act as Part of Operating System) and it's enabled, you cannot get an unrestricted/elevated token when deciding to impersonate from a non-elevated token.
Also if you then try to get the LinkedToken/Unrestricted Token of an Access Token but do not have the SeTcbPrivilege enabled, you will end up with a Token that has a SECURITY_IMPERSONATION_LEVEL of SecurityIdentification which cannot be used for impersonation. Without SECURITY_IMPERSONATION_LEVEL being SecurityImpersonation (Local Access) or SecurityDelegation (Local+Remote Access), you cannot impersonate the Unrestricted Access Token and will result in failure with LastError being ERROR_BAD_IMPERSONATION_LEVEL (Either a required impersonation level was not provided, or the provided impersonation level is invalid).
This means if you do not have SeTcbPrivilege and it is not enable on the token, it is required to have an elevated process token (ex. Run As Admin was used to start the program manually or forcefully via requestedExecutionLevel level="requireAdministrator") then Windows will not strip the privileges on Access Token when you decide to impersonate the Access Token.
By using a local administrator account that runs the program as administrator, and then using domain account credentials that are an admin on another machine to get an access token using the params of LOGON32_LOGONTYPE.LOGON32_LOGON_NEW_CREDENTIALS and LOGON32_LOGONPROVIDER.LOGON32_PROVIDER_WINNT50. It is then possible to elevate privileges on the NEWCRED token, impersonate the NEWCRED token which will not strip away the privileges, and have it successfully connect to another computer and read from a file and write to a file on another computer to which it has no permissions or is even explicitly denied, proving it was using the token backup and restore privileges!
Helpful Sources:
how do i convert restricted user token to unrestricted one?
How to call LogonUser() to get a non-restricted full token inside a Windows Service with UAC enabled?
I'm trying to develop an application, where user can load a proxy list and software should navigate to a page with different IP's. I'm using some registry settings to do that. But the problem is that when i try to change the IP address it remains the same, in "Internet Options" the IP has changed but on the web page which i'm navigating to, is still shown the IP I navigated first time with. Any solutions about that? or maybe, You have a better solution about this situation. Please suggest something. I tried "httprequest" some moments ago, but that was maddeningly unhelpful. Here's my code:
private void BrowseProxy_Click(object sender, EventArgs e)
{
openFileDialog1.ShowDialog();
ProxySrcTBX.Text = openFileDialog1.FileName;
using (StreamReader r = new StreamReader(ProxySrcTBX.Text))
{
string line;
while ((line = r.ReadLine()) != null)
{
ProxyCount++;
}
r.Close();
}
try { Proxy = File.ReadAllLines(ProxySrcTBX.Text); }
catch (Exception Exception) { MessageBox.Show(Exception.ToString()); }
}
private void SetProxy(string Proxy)
{
string key = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
RegistryKey RegKey = Registry.CurrentUser.OpenSubKey(key, true);
RegKey.SetValue("ProxyServer", Proxy);
RegKey.SetValue("ProxyEnable", 1);
RegKey.Close();
}
private void button6_Click(object sender, EventArgs e)
{
webBrowser1.ScriptErrorsSuppressed = true;
SetProxy(Proxy[ProxyIndex]);
label2.Text = Proxy[ProxyIndex];
currentUri = new Uri("https://myip.ru");
NavigateTo(currentUri);
}
public void NavigateTo(Uri url)
{
url = currentUri;
webBrowser1.Navigate(url);
}
private void Dispose_Click(object sender, EventArgs e)
{
SetProxy("");
string key = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
RegistryKey RegKey = Registry.CurrentUser.OpenSubKey(key, true);
RegKey.SetValue("ProxyEnable", 0);
RegKey.Close();
ProxyIndex++;
}
So, if I understand correctly, you want to have a web browser control, programmatically change its the IP address from which it navigates and it makes it requests through?
This can be done via Interop with WinInet (one of the components that IE and the web browser control use under the covers)
Check out this MSDN blog article
I will go ahead and post the code here so this post is self-contained.
using System;
using System.Runtime.InteropServices;
namespace SetProxy
{
public static class WinInetInterop
{
public static string applicationName;
[DllImport(“wininet.dll”, SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr InternetOpen(
string lpszAgent, int dwAccessType, string lpszProxyName,
string lpszProxyBypass, int dwFlags);
[DllImport(“wininet.dll”, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool InternetCloseHandle(IntPtr hInternet);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private struct INTERNET_PER_CONN_OPTION_LIST
{
public int Size;
// The connection to be set. NULL means LAN.
public System.IntPtr Connection;
public int OptionCount;
public int OptionError;
// List of INTERNET_PER_CONN_OPTIONs.
public System.IntPtr pOptions;
}
private enum INTERNET_OPTION
{
// Sets or retrieves an INTERNET_PER_CONN_OPTION_LIST structure that specifies
// a list of options for a particular connection.
INTERNET_OPTION_PER_CONNECTION_OPTION = 75,
// Notify the system that the registry settings have been changed so that
// it verifies the settings on the next call to InternetConnect.
INTERNET_OPTION_SETTINGS_CHANGED = 39,
// Causes the proxy data to be reread from the registry for a handle.
INTERNET_OPTION_REFRESH = 37
}
private enum INTERNET_PER_CONN_OptionEnum
{
INTERNET_PER_CONN_FLAGS = 1,
INTERNET_PER_CONN_PROXY_SERVER = 2,
INTERNET_PER_CONN_PROXY_BYPASS = 3,
INTERNET_PER_CONN_AUTOCONFIG_URL = 4,
INTERNET_PER_CONN_AUTODISCOVERY_FLAGS = 5,
INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL = 6,
INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS = 7,
INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME = 8,
INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL = 9,
INTERNET_PER_CONN_FLAGS_UI = 10
}
private const int INTERNET_OPEN_TYPE_DIRECT = 1; // direct to net
private const int INTERNET_OPEN_TYPE_PRECONFIG = 0; // read registry
/// <summary>
/// Constants used in INTERNET_PER_CONN_OPTON struct.
/// </summary>
private enum INTERNET_OPTION_PER_CONN_FLAGS
{
PROXY_TYPE_DIRECT = 0x00000001, // direct to net
PROXY_TYPE_PROXY = 0x00000002, // via named proxy
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
}
/// <summary>
/// Used in INTERNET_PER_CONN_OPTION.
/// When create a instance of OptionUnion, only one filed will be used.
/// The StructLayout and FieldOffset attributes could help to decrease the struct size.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
private struct INTERNET_PER_CONN_OPTION_OptionUnion
{
// A value in INTERNET_OPTION_PER_CONN_FLAGS.
[FieldOffset(0)]
public int dwValue;
[FieldOffset(0)]
public System.IntPtr pszValue;
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME ftValue;
}
[StructLayout(LayoutKind.Sequential)]
private struct INTERNET_PER_CONN_OPTION
{
// A value in INTERNET_PER_CONN_OptionEnum.
public int dwOption;
public INTERNET_PER_CONN_OPTION_OptionUnion Value;
}
/// <summary>
/// Sets an Internet option.
/// </summary>
[DllImport(“wininet.dll”, CharSet = CharSet.Ansi, SetLastError = true)]
private static extern bool InternetSetOption(
IntPtr hInternet,
INTERNET_OPTION dwOption,
IntPtr lpBuffer,
int lpdwBufferLength);
/// <summary>
/// Queries an Internet option on the specified handle. The Handle will be always 0.
/// </summary>
[DllImport(“wininet.dll”, CharSet = CharSet.Ansi, SetLastError = true,
EntryPoint = “InternetQueryOption”)]
private extern static bool InternetQueryOptionList(
IntPtr Handle,
INTERNET_OPTION OptionFlag,
ref INTERNET_PER_CONN_OPTION_LIST OptionList,
ref int size);
/// <summary>
/// Set the proxy server for LAN connection.
/// </summary>
public static bool SetConnectionProxy(string proxyServer )
{
IntPtr hInternet = InternetOpen(applicationName,INTERNET_OPEN_TYPE_DIRECT, null, null, 0);
//// Create 3 options.
//INTERNET_PER_CONN_OPTION[] Options = new INTERNET_PER_CONN_OPTION[3];
// Create 2 options.
INTERNET_PER_CONN_OPTION[] Options = new INTERNET_PER_CONN_OPTION[2];
// Set PROXY flags.
Options[0] = new INTERNET_PER_CONN_OPTION();
Options[0].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_FLAGS;
Options[0].Value.dwValue = (int)INTERNET_OPTION_PER_CONN_FLAGS.PROXY_TYPE_PROXY;
// Set proxy name.
Options[1] = new INTERNET_PER_CONN_OPTION();
Options[1].dwOption =
(int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_SERVER;
Options[1].Value.pszValue = Marshal.StringToHGlobalAnsi(proxyServer);
//// Set proxy bypass.
//Options[2] = new INTERNET_PER_CONN_OPTION();
//Options[2].dwOption =
// (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_BYPASS;
//Options[2].Value.pszValue = Marshal.StringToHGlobalAnsi(“local”);
//// Allocate a block of memory of the options.
//System.IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(Options[0])
// + Marshal.SizeOf(Options[1]) + Marshal.SizeOf(Options[2]));
// Allocate a block of memory of the options.
System.IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(Options[0])
+ Marshal.SizeOf(Options[1]));
System.IntPtr current = buffer;
// Marshal data from a managed object to an unmanaged block of memory.
for (int i = 0; i < Options.Length; i++)
{
Marshal.StructureToPtr(Options[i], current, false);
current = (System.IntPtr)((int)current + Marshal.SizeOf(Options[i]));
}
// Initialize a INTERNET_PER_CONN_OPTION_LIST instance.
INTERNET_PER_CONN_OPTION_LIST option_list = new INTERNET_PER_CONN_OPTION_LIST();
// Point to the allocated memory.
option_list.pOptions = buffer;
// Return the unmanaged size of an object in bytes.
option_list.Size = Marshal.SizeOf(option_list);
// IntPtr.Zero means LAN connection.
option_list.Connection = IntPtr.Zero;
option_list.OptionCount = Options.Length;
option_list.OptionError = 0;
int size = Marshal.SizeOf(option_list);
// Allocate memory for the INTERNET_PER_CONN_OPTION_LIST instance.
IntPtr intptrStruct = Marshal.AllocCoTaskMem(size);
// Marshal data from a managed object to an unmanaged block of memory.
Marshal.StructureToPtr(option_list, intptrStruct, true);
// Set internet settings.
bool bReturn = InternetSetOption(hInternet,
INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION, intptrStruct, size);
// Free the allocated memory.
Marshal.FreeCoTaskMem(buffer);
Marshal.FreeCoTaskMem(intptrStruct);
InternetCloseHandle(hInternet);
// Throw an exception if this operation failed.
if (!bReturn)
{
throw new ApplicationException(” Set Internet Option Failed!”);
}
return bReturn;
}
/// <summary>
/// Backup the current options for LAN connection.
/// Make sure free the memory after restoration.
/// </summary>
private static INTERNET_PER_CONN_OPTION_LIST GetSystemProxy()
{
// Query following options.
INTERNET_PER_CONN_OPTION[] Options = new INTERNET_PER_CONN_OPTION[3];
Options[0] = new INTERNET_PER_CONN_OPTION();
Options[0].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_FLAGS;
Options[1] = new INTERNET_PER_CONN_OPTION();
Options[1].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_SERVER;
Options[2] = new INTERNET_PER_CONN_OPTION();
Options[2].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_BYPASS;
// Allocate a block of memory of the options.
System.IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(Options[0])
+ Marshal.SizeOf(Options[1]) + Marshal.SizeOf(Options[2]));
System.IntPtr current = (System.IntPtr)buffer;
// Marshal data from a managed object to an unmanaged block of memory.
for (int i = 0; i < Options.Length; i++)
{
Marshal.StructureToPtr(Options[i], current, false);
current = (System.IntPtr)((int)current + Marshal.SizeOf(Options[i]));
}
// Initialize a INTERNET_PER_CONN_OPTION_LIST instance.
INTERNET_PER_CONN_OPTION_LIST Request = new INTERNET_PER_CONN_OPTION_LIST();
// Point to the allocated memory.
Request.pOptions = buffer;
Request.Size = Marshal.SizeOf(Request);
// IntPtr.Zero means LAN connection.
Request.Connection = IntPtr.Zero;
Request.OptionCount = Options.Length;
Request.OptionError = 0;
int size = Marshal.SizeOf(Request);
// Query internet options.
bool result = InternetQueryOptionList(IntPtr.Zero,
INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION,
ref Request, ref size);
if (!result)
{
throw new ApplicationException(” Set Internet Option Failed! “);
}
return Request;
}
/// <summary>
/// Restore the options for LAN connection.
/// </summary>
/// <param name=”request”></param>
/// <returns></returns>
public static bool RestoreSystemProxy()
{
IntPtr hInternet = InternetOpen(applicationName, INTERNET_OPEN_TYPE_DIRECT, null, null, 0);
INTERNET_PER_CONN_OPTION_LIST request = GetSystemProxy();
int size = Marshal.SizeOf(request);
// Allocate memory.
IntPtr intptrStruct = Marshal.AllocCoTaskMem(size);
// Convert structure to IntPtr
Marshal.StructureToPtr(request, intptrStruct, true);
// Set internet options.
bool bReturn = InternetSetOption(hInternet,
INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION,
intptrStruct, size);
// Free the allocated memory.
Marshal.FreeCoTaskMem(request.pOptions);
Marshal.FreeCoTaskMem(intptrStruct);
if (!bReturn)
{
throw new ApplicationException(” Set Internet Option Failed! “);
}
// Notify the system that the registry settings have been changed and cause
// the proxy data to be reread from the registry for a handle.
InternetSetOption(hInternet, INTERNET_OPTION.INTERNET_OPTION_SETTINGS_CHANGED,
IntPtr.Zero, 0);
InternetSetOption(hInternet, INTERNET_OPTION.INTERNET_OPTION_REFRESH,
IntPtr.Zero, 0);
InternetCloseHandle(hInternet);
return bReturn;
}
}
}
I have a requirement witch is to execute a vbscript located in a shared network drive. ie: \SERVER1\shared$\path\script.vbs
To connect to this shared folder I need to pass credentials ie:
DOMAIN\AdminShare
1234
This script has to run with local admin credentials. ie:
.\Administrator
1234
The user witch will execute the exe also has it's own credentials, ie:
DOMAIN\User
1234
How can I manage this scenario?
I've successfully connected to the smb with proper credentials with this class:
using System;
using System.Runtime.InteropServices;
using BOOL = System.Boolean;
using DWORD = System.UInt32;
using LPWSTR = System.String;
using NET_API_STATUS = System.UInt32;
namespace blah
{
class UNCAccess
{
// FROM: https://ericwijaya.wordpress.com/2013/02/06/access-remote-file-share-with-username-and-password-in-c/
//
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct USE_INFO_2
{
internal LPWSTR ui2_local;
internal LPWSTR ui2_remote;
internal LPWSTR ui2_password;
internal DWORD ui2_status;
internal DWORD ui2_asg_type;
internal DWORD ui2_refcount;
internal DWORD ui2_usecount;
internal LPWSTR ui2_username;
internal LPWSTR ui2_domainname;
}
[DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern NET_API_STATUS NetUseAdd(
LPWSTR UncServerName,
DWORD Level,
ref USE_INFO_2 Buf,
out DWORD ParmError);
[DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern NET_API_STATUS NetUseDel(
LPWSTR UncServerName,
LPWSTR UseName,
DWORD ForceCond);
private string sUNCPath;
private string sUser;
private string sPassword;
private string sDomain;
private int iLastError;
public UNCAccess()
{
}
public UNCAccess(string UNCPath, string User, string Domain, string Password)
{
login(UNCPath, User, Domain, Password);
}
public int LastError
{
get { return iLastError; }
}
/// <summary>
/// Logs in to the shared network with the provided credentials
/// </summary>
/// <param name="UNCPath">unc</param>
/// <param name="User">user</param>
/// <param name="Domain">domain</param>
/// <param name="Password">password</param>
/// <returns>TRUE OK, ELSE FALSE</returns>
public bool login(string UNCPath, string User, string Domain, string Password)
{
sUNCPath = UNCPath;
sUser = User;
sPassword = Password;
sDomain = Domain;
return NetUseWithCredentials();
}
private bool NetUseWithCredentials()
{
uint returncode;
try
{
USE_INFO_2 useinfo = new USE_INFO_2();
useinfo.ui2_remote = sUNCPath;
useinfo.ui2_username = sUser;
useinfo.ui2_domainname = sDomain;
useinfo.ui2_password = sPassword;
useinfo.ui2_asg_type = 0;
useinfo.ui2_usecount = 1;
uint paramErrorIndex;
returncode = NetUseAdd(null, 2, ref useinfo, out paramErrorIndex);
iLastError = (int)returncode;
return returncode == 0;
}
catch
{
iLastError = Marshal.GetLastWin32Error();
return false;
}
}
///
/// Closes the UNC share
///
/// True if closing was successful
public bool NetUseDelete()
{
uint returncode;
try
{
returncode = NetUseDel(null, sUNCPath, 2);
iLastError = (int)returncode;
return (returncode == 0);
}
catch
{
iLastError = Marshal.GetLastWin32Error();
return false;
}
}
}
}
Then I did a process start to run the script as admin:
Process p = new Process();
p.StartInfo.FileName = "cscript.exe";
p.StartInfo.WorkingDirectory = #"c:\";
p.StartInfo.Arguments = "//B //Nologo " + script.FullName
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.Verb = "runas";
p.StartInfo.UserName = localAdminAccount;
System.Security.SecureString pwd = new System.Security.SecureString();
foreach (char c in localAdminPasswd) { pwd.AppendChar(c); }
p.StartInfo.Password = pwd;
The problem is that when the process starts with the new credentials (localAdmin) I'm not able to find the script (the user has changed so no access to the shared network).
I though it was because of the user, so I've also tried to create a launcher to elevate the privileges of the execution of the main application without user interaction (another process.start from the launcher), which works fine, but then the same thing happens (not found).
Any help on this? Thanks
I've solved it with two launchers to get the privileges and a runas in the process start, so now i can run the script with the necesary credentials =)
I have a somewhat unusual situation.
I am dealing with IIS 7.5 installed on a Windows Server 2008 R2 Domain Controller machine.
I am trying to write a C# program that installs my web application on that IIS. Everything works OK, except the moment when I need to assign access permissions for the Application Pool to the folder where the web app is installed.
After having done some research I found that I need to assign access for the following user account:
IIS AppPool\[AppPoolName]
So I came up with this code:
setFolderPermissions(#"C:\inetpub\www_test1",
#"IIS AppPool\" + strAppPoolName,
System.Security.AccessControl.FileSystemRights.Read | System.Security.AccessControl.FileSystemRights.ListDirectory,
System.Security.AccessControl.AccessControlType.Allow);
public static string setFolderPermissions(string strFolderPath, string sUserName, FileSystemRights rights, AccessControlType access)
{
DirectoryInfo info = new DirectoryInfo(strFolderPath);
DirectorySecurity ds = info.GetAccessControl();
ds.AddAccessRule(new FileSystemAccessRule(sUserName,
rights,
InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit,
PropagationFlags.None,
access));
info.SetAccessControl(ds);
}
The method above works except when it is useed on a Domain Controller. It throws this exception:
Some or all identity references could not be translated.
And the only way how I can assign those required permissions is by doing this manually from a command line:
C:\Users\Administrator>icacls "C:\inetpub\www_test1" /grant "IIS AppPool\MyAppsoolName":(CI)(OI)(M)
Any idea how to do this icacls stuff with C#?
Better, create your own application pool that runs using the given credential. And the assign the permissions for the given credential (user name) on the web application installed folder.
You shouldn’t be running IIS on domain controller as advised my Microsoft as this may cause issues with security and permissions.
If you really want to do this you should use domain account to run application pool, not integrated IIS Apppool. This doesn’t work because domain controller doesn’t have local accounts and IIS APPool accounts are local.
Check this for more details
http://blogs.technet.com/b/abizerh/archive/2009/07/16/should-iis-be-installed-on-domain-controller.aspx
I think I got it using an unmanaged approach. It turns out kinda ugly in C#, but here's the concept:
uint nOSErr;
if(!(nOSErr = setFolderPermissions(#"C:\inetpub\www_test1", #"IIS AppPool\" + strAppPoolName, OSFileAccess.FILE_GENERIC_READ)))
{
throw new Exception("Failed to change permissions, error code=" + nOSErr);
}
public static uint setFolderPermissions(string strFolderPath, string strUserName, OSFileAccess access)
{
//Set folder permissions
//RETURN:
// = 0 if success
// = Otherwise error code -- check GetLastError
uint dwRes = 0;
try
{
IntPtr pZero = IntPtr.Zero;
IntPtr pSecDesc = pZero;
IntPtr pDacl = pZero;
if ((dwRes = GetNamedSecurityInfo(strFolderPath, SE_OBJECT_TYPE.SE_FILE_OBJECT, SECURITY_INFORMATION.DACL_SECURITY_INFORMATION,
out pZero, out pZero, out pDacl, out pZero, out pSecDesc)) == ERROR_SUCCESS)
{
try
{
EXPLICIT_ACCESS ea = new EXPLICIT_ACCESS();
ea.grfAccessPermissions = access;
ea.grfAccessMode = AccessMode.GRANT_ACCESS;
ea.grfInheritance = AceFlags.CONTAINER_INHERIT_ACE | AceFlags.OBJECT_INHERIT_ACE;
ea.Trustee.MultipleTrusteeOperation = UIntPtr.Zero;
ea.Trustee.pMultipleTrustee = UIntPtr.Zero;
ea.Trustee.TrusteeForm = (UIntPtr)(TrusteeForm.TRUSTEE_IS_NAME);
ea.Trustee.ptstrName = strUserName;
IntPtr pNewDacl = pZero;
if((dwRes = SetEntriesInAcl(1, ref ea, pDacl, out pNewDacl)) == ERROR_SUCCESS)
{
try
{
if ((dwRes = SetNamedSecurityInfo(strFolderPath, SE_OBJECT_TYPE.SE_FILE_OBJECT, SECURITY_INFORMATION.DACL_SECURITY_INFORMATION,
IntPtr.Zero, IntPtr.Zero, pNewDacl, IntPtr.Zero)) == ERROR_SUCCESS)
{
//Done
}
}
finally
{
//Free mem
if (pNewDacl != IntPtr.Zero)
{
LocalFree(pNewDacl);
pNewDacl = IntPtr.Zero;
}
}
}
}
finally
{
//Free mem
if (pSecDesc != IntPtr.Zero)
{
LocalFree(pSecDesc);
pSecDesc = IntPtr.Zero;
}
}
}
}
catch
{
dwRes = ERROR_INVALID_DATA;
}
return dwRes;
}
enum SE_OBJECT_TYPE
{
SE_UNKNOWN_OBJECT_TYPE = 0,
SE_FILE_OBJECT,
SE_SERVICE,
SE_PRINTER,
SE_REGISTRY_KEY,
SE_LMSHARE,
SE_KERNEL_OBJECT,
SE_WINDOW_OBJECT,
SE_DS_OBJECT,
SE_DS_OBJECT_ALL,
SE_PROVIDER_DEFINED_OBJECT,
SE_WMIGUID_OBJECT,
SE_REGISTRY_WOW64_32KEY
}
[Flags]
enum SECURITY_INFORMATION : uint
{
OWNER_SECURITY_INFORMATION = 0x00000001,
GROUP_SECURITY_INFORMATION = 0x00000002,
DACL_SECURITY_INFORMATION = 0x00000004,
SACL_SECURITY_INFORMATION = 0x00000008,
UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000,
UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000,
PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000,
PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
static extern uint GetNamedSecurityInfo(
string pObjectName,
SE_OBJECT_TYPE ObjectType,
SECURITY_INFORMATION SecurityInfo,
out IntPtr pSidOwner,
out IntPtr pSidGroup,
out IntPtr pDacl,
out IntPtr pSacl,
out IntPtr pSecurityDescriptor);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr LocalFree(IntPtr hMem);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 0)] //Platform independent (32 & 64 bit) - use Pack = 0 for both platforms. IntPtr works as well.
internal struct TRUSTEE
{
internal UIntPtr pMultipleTrustee; // must be null
internal UIntPtr MultipleTrusteeOperation;
internal UIntPtr TrusteeForm;
internal UIntPtr TrusteeType;
//[MarshalAs(UnmanagedType.LPStr)]
internal string ptstrName;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct EXPLICIT_ACCESS
{
internal OSFileAccess grfAccessPermissions;
internal AccessMode grfAccessMode;
internal AceFlags grfInheritance;
internal TRUSTEE Trustee;
}
private const uint ERROR_SUCCESS = 0;
[Flags]
public enum OSFileAccess : uint
{
AccessSystemSecurity = 0x1000000, // AccessSystemAcl access type
MaximumAllowed = 0x2000000, // MaximumAllowed access type
Delete = 0x10000,
ReadControl = 0x20000,
WriteDAC = 0x40000,
WriteOwner = 0x80000,
Synchronize = 0x100000,
StandardRightsRequired = 0xF0000,
StandardRightsRead = ReadControl,
StandardRightsWrite = ReadControl,
StandardRightsExecute = ReadControl,
StandardRightsAll = 0x1F0000,
SpecificRightsAll = 0xFFFF,
FILE_READ_DATA = 0x0001, // file & pipe
FILE_LIST_DIRECTORY = 0x0001, // directory
FILE_WRITE_DATA = 0x0002, // file & pipe
FILE_ADD_FILE = 0x0002, // directory
FILE_APPEND_DATA = 0x0004, // file
FILE_ADD_SUBDIRECTORY = 0x0004, // directory
FILE_CREATE_PIPE_INSTANCE = 0x0004, // named pipe
FILE_READ_EA = 0x0008, // file & directory
FILE_WRITE_EA = 0x0010, // file & directory
FILE_EXECUTE = 0x0020, // file
FILE_TRAVERSE = 0x0020, // directory
FILE_DELETE_CHILD = 0x0040, // directory
FILE_READ_ATTRIBUTES = 0x0080, // all
FILE_WRITE_ATTRIBUTES = 0x0100, // all
GENERIC_READ = 0x80000000,
GENERIC_WRITE = 0x40000000,
GENERIC_EXECUTE = 0x20000000,
GENERIC_ALL = 0x10000000,
SPECIFIC_RIGHTS_ALL = 0x00FFFF,
FILE_ALL_ACCESS = StandardRightsRequired | Synchronize | 0x1FF,
FILE_GENERIC_READ = StandardRightsRead | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | Synchronize,
FILE_GENERIC_WRITE = StandardRightsWrite | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | Synchronize,
FILE_GENERIC_EXECUTE = StandardRightsExecute | FILE_READ_ATTRIBUTES | FILE_EXECUTE | Synchronize
}
internal enum AccessMode
{
NOT_USED_ACCESS = 0,
GRANT_ACCESS,
SET_ACCESS,
DENY_ACCESS,
REVOKE_ACCESS,
SET_AUDIT_SUCCESS,
SET_AUDIT_FAILURE
}
[Flags]
internal enum AceFlags
{
OBJECT_INHERIT_ACE = 0x1,
CONTAINER_INHERIT_ACE = 0x2,
NO_PROPAGATE_INHERIT_ACE = 0x4,
INHERIT_ONLY_ACE = 0x8,
INHERITED_ACE = 0x10,
SUCCESSFUL_ACCESS_ACE_FLAG = 0x40,
FAILED_ACCESS_ACE_FLAG = 0x80
}
enum TrusteeForm
{
TRUSTEE_IS_SID,
TRUSTEE_IS_NAME,
TRUSTEE_BAD_FORM,
TRUSTEE_IS_OBJECTS_AND_SID,
TRUSTEE_IS_OBJECTS_AND_NAME
}
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint SetEntriesInAcl(
int cCountOfExplicitEntries,
ref EXPLICIT_ACCESS pListOfExplicitEntries,
IntPtr OldAcl,
out IntPtr NewAcl);
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
static extern uint SetNamedSecurityInfo(
string pObjectName,
SE_OBJECT_TYPE ObjectType,
SECURITY_INFORMATION SecurityInfo,
IntPtr psidOwner,
IntPtr psidGroup,
IntPtr pDacl,
IntPtr pSacl);
I want to start a Process with Admin rights. When I run the code below the Process complains saying it needs Admin rights:
public class ImpersonationHelper : IDisposable
{
IntPtr m_tokenHandle = new IntPtr(0);
WindowsImpersonationContext m_impersonatedUser;
#region Win32 API Declarations
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2; //This parameter causes LogonUser to create a primary token.
[DllImport("advapi32.dll", 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)]
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);
#endregion
/// <summary>
/// Constructor. Impersonates the requested user. Impersonation lasts until
/// the instance is disposed.
/// </summary>
public ImpersonationHelper(string domain, string user, string password)
{
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(user, domain, password,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
ref m_tokenHandle);
if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(ret);
}
// Impersonate
m_impersonatedUser = new WindowsIdentity(m_tokenHandle).Impersonate();
}
#region IDisposable Pattern
/// <summary>
/// Revert to original user and cleanup.
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// Revert to original user identity
if (m_impersonatedUser != null)
m_impersonatedUser.Undo();
}
// Free the tokens.
if (m_tokenHandle != IntPtr.Zero)
CloseHandle(m_tokenHandle);
}
/// <summary>
/// Explicit dispose.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Destructor
/// </summary>
~ImpersonationHelper()
{
Dispose(false);
}
#endregion
}
using (new ImpersonationHelper("xxx.blabla.com", "xxxx", "xxxx"))
{
if (!string.IsNullOrEmpty(txtFilename.Text))
Process.Start(txtFilename.Text);
}
Can you try something like this: Start a new Process as another user
Code sample:
System.Diagnostics.Process proc = new System.Diagnostics.Process();
System.Security.SecureString ssPwd = new System.Security.SecureString();
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.FileName = "filename";
proc.StartInfo.Arguments = "args...";
proc.StartInfo.Domain = "domainname";
proc.StartInfo.UserName = "username";
string password = "user entered password";
for (int x = 0; x < password.Length; x++)
{
ssPwd.AppendChar(password[x]);
}
password = "";
proc.StartInfo.Password = ssPwd;
proc.Start();
Correct usage of SecureString and a few extras:
//You should use SecureString like the following
SecureString password = new SecureString();
password.AppendChar('p');
password.AppendChar('a');
password.AppendChar('s');
password.AppendChar('s');
Process process = new Process();
process.StartInfo.UseShellExecute = false;
//Set the working directory if you don't execute something like calc or iisreset but your own exe in which you want to access some files etc..
process.StartInfo.WorkingDirectory = "workingDirectory";
//Full path (e.g. it can be #"C:\Windows\System32\iisreset.exe" OR you can use only file name if the path is included in Environment Variables..)
process.StartInfo.FileName = #"fileName";
process.StartInfo.Domain = "domain";
process.StartInfo.UserName = "userName";
process.StartInfo.Password = password;
process.Start();
EDIT: I don't know why this answer is voted below 0, maybe a little more explanation is needed. If you'll use this in non-interactive environment (like a web application) and you want to run a process with a user, then you have a few options to use the user's password. You can either read password from a storage or from code. A better way; you can store it encrypted. However, if you plan to use it in plain form (maybe temporarily or just to test something etc.), you can use SecureString in a way I described. The accepted answer doesn't use SecureString in a right way. Reading password into a string from console and then putting it into a SecureString is simply WRONG. The accepted answer does NOT secure that string or something but only cheating it. That was the main motivation for me to add this answer. Check link.
Don't forget to add LoadUserProfile=true since without this access denied issues might occur if your application is doing some read write operations