using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace AccessToken
{
class Safer
{
/* Start off with the PInvoke Declarations */
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public long nLength;
public long lpSecurityDescriptor;
public long bInheritHandle;
}
[Flags]
public enum SaferLevelEnum
{
Disallowed = 0,
Untrusted = 0x1000,
Constrained = 0x10000,
NormalUser = 0x20000,
FullyTrusted = 0x40000
}
[Flags]
public enum SaferScopeEnum
{
Machine = 1,
User = 2
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
protected struct STARTUPINFO
{
public Int32 cb;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpReserved;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpDesktop;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
/// <summary>
/// PInvoked version of SaferCreateLevel. Try not to use these.
/// </summary>
[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool SaferCreateLevel(SaferScopeEnum dwScopeId, SaferLevelEnum dwLevelId, int OpenFlags,
out IntPtr pLevelHandle, IntPtr lpReserved);
/// <summary>
/// PInvoked version of SaferComputeTokenLevel. Try not to use these.
/// </summary>
[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool SaferComputeTokenFromLevel(IntPtr LevelHandle, IntPtr InAccessToken, out IntPtr OutAccessToken,
int dwFlags, IntPtr lpReserved);
/// <summary>
/// PInvoked version of SaferCloseLevel. Try not to use these.
/// </summary>
[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool SaferCloseLevel(IntPtr hLevelHandle);
[DllImport("kernel32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool CloseHandle(IntPtr hObject);
/// <summary>
/// Pinvoke version of CreateProcessAsUser (needed because Process.Start is not compatible
/// with Safer).
/// </summary>
/// <returns>the result from CreateProcessAsUser</returns>
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
protected static extern bool CreateProcessAsUser(IntPtr hToken, string lpApplicationName,
string lpCommandLine, IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes, Boolean bInheritHandles, uint dwCreationFlags,
IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
/// <summary>
/// Provides a managed frontend for the SAFER CreateProcessAsUser(). Stdio redirection is not supported.
/// </summary>
/// <param name="startInfo">The startInfo you'd normally send to Process.Start</param>
/// <returns>the status of CreateProcess()</returns>
public bool StartProcess(System.Diagnostics.ProcessStartInfo startInfo)
{
/* Dissect the startInfo and translate it to relevant CPAU values */
//IntPtr lpEnv = Dictionary2lpvoid(startInfo.EnvironmentVariables);
IntPtr lpEnv = IntPtr.Zero;
STARTUPINFO lpStartupInfo = new STARTUPINFO();
uint dwCreationFlags = 0x00000020;
lpStartupInfo.cb = Marshal.SizeOf(lpStartupInfo);
lpStartupInfo.dwFlags = 1; /* USESHOWWINDOW */
switch (startInfo.WindowStyle)
{/* Translate WindowStyle to wShowWindow, because they are incompatible. */
case System.Diagnostics.ProcessWindowStyle.Hidden:
{
lpStartupInfo.wShowWindow = 0;
break;
}
case System.Diagnostics.ProcessWindowStyle.Maximized:
{
lpStartupInfo.wShowWindow = 3;
break;
}
case System.Diagnostics.ProcessWindowStyle.Minimized:
{
lpStartupInfo.wShowWindow = 6;
break;
}
default:
case System.Diagnostics.ProcessWindowStyle.Normal:
{
lpStartupInfo.wShowWindow = 1;
break;
}
}
if(startInfo.CreateNoWindow)
dwCreationFlags = dwCreationFlags | 0x08000000;
if (startInfo.ErrorDialog == false)
dwCreationFlags = (uint)((long)(dwCreationFlags) & ~(0x4000000));
string lpCommandLine = startInfo.FileName + " " + startInfo.Arguments;
this.lpProcInfo = new PROCESS_INFORMATION();
bool Result = CreateProcessAsUser(this.Token_, startInfo.FileName,
lpCommandLine, IntPtr.Zero, IntPtr.Zero, false, dwCreationFlags, lpEnv,
startInfo.WorkingDirectory, ref lpStartupInfo, out this.lpProcInfo_);
/* Free the buffer here */
//Marshal.FreeHGlobal(lpEnv);
return Result;
}
//I couldn't set the Environmentblock in ProcessStartInfo, so couldn't test this function.
//I don't think it was stable anyway.
//private unsafe IntPtr Dictionary2lpvoid(System.Collections.Specialized.StringDictionary stringDictionary)
//{
// /* First find the total size of the string dictionary */
// int requiredSize = 1;
// foreach (System.Collections.DictionaryEntry envEntry in stringDictionary)
// {
// /** each string is of form Key=Value.
// * 1 for =, 1 for \0, sizeof(Key) for Key, sizeof(Value) for Value.
// * and one more \0 at the end.
// **/
// requiredSize += Convert.ToString(envEntry.Key).Length;
// requiredSize += 2;
// requiredSize += Convert.ToString(envEntry.Value).Length;
// }
// char* lpEnv = (char*)(Marshal.AllocHGlobal(requiredSize));
// int i = 0;
//
// foreach (System.Collections.DictionaryEntry envEntry in stringDictionary)
// {
// char[] keyEnv = Convert.ToString(envEntry.Key).ToCharArray();
// char[] valEnv = Convert.ToString(envEntry.Value).ToCharArray();
//
// Marshal.Copy(keyEnv, 0, (IntPtr)(lpEnv + i), keyEnv.Length);
// i += keyEnv.Length;
// Marshal.Copy("=".ToCharArray(), 0, (IntPtr)(lpEnv + i), 1);
// i += 1;
// Marshal.Copy(valEnv, 0, (IntPtr)(lpEnv + i), valEnv.Length);
// i += valEnv.Length;
// Marshal.Copy("\0".ToCharArray(), 0, (IntPtr)(lpEnv + i), 1);
// i += 1;
// }
// Marshal.Copy("\0".ToCharArray(), 0, (IntPtr)(lpEnv + i), 1);
// i += 1;
// return (IntPtr)(lpEnv);
// /* Don't forget to free the returned buffer. */
//}
protected IntPtr LevelHandle_;
protected IntPtr Token_;
private PROCESS_INFORMATION lpProcInfo_;
public PROCESS_INFORMATION lpProcInfo
{
get {return lpProcInfo;}
set
{
IntPtr InvalidHandleValue = new IntPtr(1);
if (lpProcInfo_.hProcess != null && lpProcInfo_.hProcess != InvalidHandleValue &&
lpProcInfo_.hProcess != value.hProcess)
{
CloseHandle(lpProcInfo_.hProcess);
}
if (lpProcInfo_.hThread != null && lpProcInfo_.hThread != InvalidHandleValue &&
lpProcInfo_.hThread != value.hThread)
{
CloseHandle(lpProcInfo_.hProcess);
}
lpProcInfo_ = value;
}
}
/// <summary>
/// Creates a Safer Level Handle using the current process and user as a template.
/// </summary>
public Safer()
{
InitHelper(SaferLevelEnum.NormalUser, null);
}
~Safer()
{
SaferCloseLevel(this.LevelHandle_);
}
/// <summary>
/// Creates a Safer Level Handle from the specified access token.
/// </summary>
/// <param name="dwScopeIdIn">the safer level you want to use (defaults to NormalUser)</param>
/// <param name="hTokenIn">the access token with which you want to build from (defaults to current token)</param>
public Safer(SaferLevelEnum dwScopeIdIn, ManagedTokenHandle hTokenIn)
{
InitHelper(dwScopeIdIn, hTokenIn);
}
private void InitHelper(SaferLevelEnum dwScopeIdIn, ManagedTokenHandle hTokenIn)
{
if (hTokenIn != null)
{/* use NULL for the token as default. */
this.Token_ = hTokenIn.HandleInternal;
}
else
{
this.Token_ = IntPtr.Zero;
}
if (!SaferCreateLevel(SaferScopeEnum.User, dwScopeIdIn, 1, out LevelHandle_, IntPtr.Zero))
{
throw new System.ComponentModel.Win32Exception();
}
if (!SaferComputeTokenFromLevel(this.LevelHandle_, this.Token_, out this.Token_, 0, IntPtr.Zero))
{
throw new System.ComponentModel.Win32Exception();
}
}
}
}