Solved

Run c# Code as Logged on User

Posted on 2009-07-07
5
877 Views
Last Modified: 2013-12-17
I'm using the Impersonate command to print a report as a domain user.

The problem is that if I use the Windows Service on another machine, I have to change the user that is impersonated.

How can I change the code to always run as the currently logged on user, so it uses their default printer.
namespace Tools

{

	#region Using directives.

	// ----------------------------------------------------------------------

 

	using System;

	using System.Security.Principal;

	using System.Runtime.InteropServices;

	using System.ComponentModel;

 

	// ----------------------------------------------------------------------

	#endregion

 

	/////////////////////////////////////////////////////////////////////////

 

	/// <summary>

	/// Impersonation of a user. Allows to execute code under another

	/// user context.

	/// Please note that the account that instantiates the Impersonator class

	/// needs to have the 'Act as part of operating system' privilege set.

	/// </summary>

	/// <remarks>	

	/// This class is based on the information in the Microsoft knowledge base

	/// article http://support.microsoft.com/default.aspx?scid=kb;en-us;Q306158

	/// 

	/// Encapsulate an instance into a using-directive like e.g.:

	/// 

	///		...

	///		using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) )

	///		{

	///			...

	///			[code that executes under the new context]

	///			...

	///		}

	///		...

	/// 

	/// Please contact the author Uwe Keim (mailto:uwe.keim@zeta-software.de)

	/// for questions regarding this class.

	/// </remarks>

	public class Impersonator :

		IDisposable

	{

		#region Public methods.

		// ------------------------------------------------------------------

 

		/// <summary>

		/// Constructor. Starts the impersonation with the given credentials.

		/// Please note that the account that instantiates the Impersonator class

		/// needs to have the 'Act as part of operating system' privilege set.

		/// </summary>

		/// <param name="userName">The name of the user to act as.</param>

		/// <param name="domainName">The domain name of the user to act as.</param>

		/// <param name="password">The password of the user to act as.</param>

		public Impersonator(

			string userName,

			string domainName,

			string password )

		{

			ImpersonateValidUser( userName, domainName, password );

		}

 

		// ------------------------------------------------------------------

		#endregion

 

		#region IDisposable member.

		// ------------------------------------------------------------------

 

		public void Dispose()

		{

			UndoImpersonation();

		}

 

		// ------------------------------------------------------------------

		#endregion

 

		#region P/Invoke.

		// ------------------------------------------------------------------

 

		[DllImport("advapi32.dll", SetLastError=true)]

		private static extern int LogonUser(

			string lpszUserName,

			string lpszDomain,

			string lpszPassword,

			int dwLogonType,

			int dwLogonProvider,

			ref IntPtr phToken);

		

		[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]

		private static extern int DuplicateToken(

			IntPtr hToken,

			int impersonationLevel,

			ref IntPtr hNewToken);

 

		[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]

		private static extern bool RevertToSelf();

 

		[DllImport("kernel32.dll", CharSet=CharSet.Auto)]

		private static extern  bool CloseHandle(

			IntPtr handle);

 

		private const int LOGON32_LOGON_INTERACTIVE = 2;

		private const int LOGON32_PROVIDER_DEFAULT = 0;

 

		// ------------------------------------------------------------------

		#endregion

 

		#region Private member.

		// ------------------------------------------------------------------

 

		/// <summary>

		/// Does the actual impersonation.

		/// </summary>

		/// <param name="userName">The name of the user to act as.</param>

		/// <param name="domainName">The domain name of the user to act as.</param>

		/// <param name="password">The password of the user to act as.</param>

		private void ImpersonateValidUser(

			string userName, 

			string domain, 

			string password )

		{

			WindowsIdentity tempWindowsIdentity = null;

			IntPtr token = IntPtr.Zero;

			IntPtr tokenDuplicate = IntPtr.Zero;

 

			try

			{

				if ( RevertToSelf() )

				{

					if ( LogonUser(

						userName, 

						domain, 

						password, 

						LOGON32_LOGON_INTERACTIVE,

						LOGON32_PROVIDER_DEFAULT, 

						ref token ) != 0 )

					{

						if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 )

						{

							tempWindowsIdentity = new WindowsIdentity( tokenDuplicate );

							impersonationContext = tempWindowsIdentity.Impersonate();

						}

						else

						{

							throw new Win32Exception( Marshal.GetLastWin32Error() );

						}

					}

					else

					{

						throw new Win32Exception( Marshal.GetLastWin32Error() );

					}

				}

				else

				{

					throw new Win32Exception( Marshal.GetLastWin32Error() );

				}

			}

			finally

			{

				if ( token!= IntPtr.Zero )

				{

					CloseHandle( token );

				}

				if ( tokenDuplicate!=IntPtr.Zero )

				{

					CloseHandle( tokenDuplicate );

				}

			}

		}

 

		/// <summary>

		/// Reverts the impersonation.

		/// </summary>

		private void UndoImpersonation()

		{

			if ( impersonationContext!=null )

			{

				impersonationContext.Undo();

			}	

		}

 

		private WindowsImpersonationContext impersonationContext = null;

 

		// ------------------------------------------------------------------

		#endregion

	}

 

	/////////////////////////////////////////////////////////////////////////

}

Open in new window

0
Comment
Question by:wint100
  • 3
  • 2
5 Comments
 
LVL 2

Expert Comment

by:Wegelin
Comment Utility
Hi
When you need the token from the loggedin User take this
You not need the User and Password
I do this over the Process explorer
with my class you can Start a Process from a System Service with the Loggedin User
ProcessAsUser.Launch("cmdline")


for your Problem maybe you can this do so:

WindowsImpersonationContext ImpersonationCtx = WindowsIdentity.Impersonate(ProcessAsUser.GetUserToken);

//Do something under the context of the impersonated user.

 ImpersonationCtx.Undo();


the snippet is in VB.net you can easy translate to c#
http://www.carlosag.net/Tools/CodeTranslator/









Imports System.Runtime.InteropServices

Imports System.Security.Permissions

Imports System.Reflection

Public Class ProcessAsUser

    <StructLayout(LayoutKind.Sequential)> _

Friend Structure PROCESS_INFORMATION

        Public hProcess As IntPtr

        Public hThread As IntPtr

        Public dwProcessId As Int32

        Public dwThreadId As Int32

    End Structure

    <StructLayout(LayoutKind.Sequential)> _

    Friend Structure SECURITY_ATTRIBUTES

        Public nLength As Int32

        Public lpSecurityDescriptor As IntPtr

        Public bInheritHandle As Boolean

    End Structure
 

    <StructLayout(LayoutKind.Sequential)> _

    Public Structure STARTUPINFO

        Public cb As Int32

        Public lpReserved As String

        Public lpDesktop As String

        Public lpTitle As String

        Public dwX As Int32

        Public dwY As Int32

        Public dwXSize As Int32

        Public dwYSize As Int32

        Public dwXCountChars As Int32

        Public dwYCountChars As Int32

        Public dwFillAttribute As Int32

        Public dwFlags As Int32

        Public wShowWindow As Short

        Public cbReserved2 As Short

        Public lpReserved2 As IntPtr

        Public hStdInput As IntPtr

        Public hStdOutput As IntPtr

        Public hStdError As IntPtr

    End Structure
 

    Public Enum SECURITY_IMPERSONATION_LEVEL

        SecurityAnonymous

        SecurityIdentification

        SecurityImpersonation

        SecurityDelegation

    End Enum

    Public Enum TOKEN_TYPE
 

        TokenPrimary = 1
 

        TokenImpersonation

    End Enum
 

    Private Shared STANDARD_RIGHTS_REQUIRED As UInteger = 983040
 

    Private Shared STANDARD_RIGHTS_READ As UInteger = 131072
 

    Private Shared TOKEN_ASSIGN_PRIMARY As UInteger = 1
 

    Private Shared TOKEN_DUPLICATE As UInteger = 2
 

    Private Shared TOKEN_IMPERSONATE As UInteger = 4
 

    Private Shared TOKEN_QUERY As UInteger = 8
 

    Private Shared TOKEN_QUERY_SOURCE As UInteger = 16
 

    Private Shared TOKEN_ADJUST_PRIVILEGES As UInteger = 32
 

    Private Shared TOKEN_ADJUST_GROUPS As UInteger = 64
 

    Private Shared TOKEN_ADJUST_DEFAULT As UInteger = 128
 

    Private Shared TOKEN_ADJUST_SESSIONID As UInteger = 256
 

    Private Shared TOKEN_READ As UInteger = (STANDARD_RIGHTS_READ Or TOKEN_QUERY)

    Private Shared TOKEN_ALL_ACCESS As UInteger = (STANDARD_RIGHTS_REQUIRED _

                   Or (TOKEN_ASSIGN_PRIMARY _

                   Or (TOKEN_DUPLICATE _

                   Or (TOKEN_IMPERSONATE _

                   Or (TOKEN_QUERY _

                   Or (TOKEN_QUERY_SOURCE _

                   Or (TOKEN_ADJUST_PRIVILEGES _

                   Or (TOKEN_ADJUST_GROUPS _

                   Or (TOKEN_ADJUST_DEFAULT Or TOKEN_ADJUST_SESSIONID)))))))))
 

    ' Private Declare Auto Function CreateProcessAsUser Lib "advapi32.dll" (ByVal token As IntPtr, ByVal applicationName As String, ByVal commandLine As String, ByRef processAttributes As SECURITY_ATTRIBUTES, ByRef threadAttributes As SECURITY_ATTRIBUTES, ByVal inheritHandles As Boolean, ByVal creationFlags As UInteger, ByVal environment As IntPtr, ByVal currentDirectory As String, ByRef startupInfo As STARTUPINFO, ByRef processInformation As PROCESS_INFORMATION) As Boolean

    Private Declare Auto Function CreateProcessAsUser Lib "advapi32" ( _

      ByVal hToken As IntPtr, _

      ByVal strApplicationName As String, _

      ByVal strCommandLine As String, _

      ByRef lpProcessAttributes As SECURITY_ATTRIBUTES, _

      ByRef lpThreadAttributes As SECURITY_ATTRIBUTES, _

      ByVal bInheritHandles As Boolean, _

      ByVal dwCreationFlags As Integer, _

      ByVal lpEnvironment As IntPtr, _

      ByVal lpCurrentDriectory As String, _

      ByRef lpStartupInfo As STARTUPINFO, _

      ByRef lpProcessInformation As PROCESS_INFORMATION) As Boolean
 

    Public Declare Function DuplicateTokenEx Lib "advapi32.dll" (ByVal existingToken As IntPtr, ByVal desiredAccess As UInteger, ByVal tokenAttributes As IntPtr, ByVal impersonationLevel As SECURITY_IMPERSONATION_LEVEL, ByVal tokenType As TOKEN_TYPE, ByRef newToken As IntPtr) As Boolean

    Declare Function CreateEnvironmentBlock Lib "userenv.dll" (ByRef lpEnvironment As IntPtr, ByVal hToken As IntPtr, ByVal bInherit As Boolean) As Boolean

    Declare Function OpenProcessToken Lib "advapi32.dll" (ByVal ProcessHandle As IntPtr, ByVal DesiredAccess As Integer, ByRef TokenHandle As IntPtr) As Boolean

    Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Boolean
 

    Declare Function DestroyEnvironmentBlock Lib "userenv.dll" (ByVal lpEnvironment As IntPtr) As Boolean

    Private Const SW_SHOW As Short = 5

    Private Const SW_SHOWMAXIMIZED As Short = 7

    ' Private Const TOKEN_QUERY = 8

    'Private Const TOKEN_DUPLICATE = 2

    'Private Const TOKEN_ASSIGN_PRIMARY = 1

    Private Const GENERIC_ALL_ACCESS = 268435456

    Private Const STARTF_USESHOWWINDOW = 1

    Private Const STARTF_FORCEONFEEDBACK = 64

    Private Const CREATE_UNICODE_ENVIRONMENT = 1024

    Public Shared ErrStr As String = ""
 

    Private Shared Function LaunchProcessAsUser(ByVal cmdLine As String, ByVal _

      token As IntPtr, ByVal envBlock As IntPtr) As Boolean

        Dim result As Boolean = False

        Dim pi As PROCESS_INFORMATION = New PROCESS_INFORMATION

        Dim saProcess As SECURITY_ATTRIBUTES = New SECURITY_ATTRIBUTES

        Dim saThread As SECURITY_ATTRIBUTES = New SECURITY_ATTRIBUTES
 

        saProcess.nLength = Marshal.SizeOf(saProcess)

        saThread.nLength = Marshal.SizeOf(saThread)

        Dim si As STARTUPINFO = New STARTUPINFO

        si.cb = Marshal.SizeOf(si)

        'si.lpDesktop = "WinSta0\Default"

        si.lpDesktop = Nothing

        si.dwFlags = STARTF_USESHOWWINDOW Or STARTF_FORCEONFEEDBACK

        si.wShowWindow = SW_SHOW

        result = CreateProcessAsUser(token, cmdLine, "", saProcess, saThread, _

          False, CREATE_UNICODE_ENVIRONMENT, envBlock, Nothing, si, pi)

        If result = False Then

            Dim iError As Integer = Marshal.GetLastWin32Error

            ErrStr = String.Format("CreateProcessAsUser Error:" & _

              "{0}", iError)

            ' Debug.WriteLine(message)

        Else

            CloseHandle(token)

        End If

        Return result

    End Function
 

    Private Shared Function GetPrimaryToken(ByVal processId As Integer) As  _

      IntPtr

        Dim token As IntPtr = IntPtr.Zero

        Dim primaryToken As IntPtr = IntPtr.Zero

        Dim retVal As Boolean = False

        Dim p As Process = Nothing

        Try

            p = Process.GetProcessById(processId)

        Catch generatedExceptionVariable0 As ArgumentException

            Dim details As String = String.Format("ProcessID {0} Not" & _

              "Available", processId)

            Debug.WriteLine(details)

            Return primaryToken

        End Try

        retVal = OpenProcessToken(p.Handle, TOKEN_DUPLICATE, token)

        If retVal = True Then

            Dim sa As SECURITY_ATTRIBUTES = New SECURITY_ATTRIBUTES

            sa.nLength = Marshal.SizeOf(sa)

            retVal = DuplicateTokenEx(token, TOKEN_ASSIGN_PRIMARY Or _

              TOKEN_DUPLICATE Or TOKEN_QUERY, IntPtr.Zero, CType( _

              SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, Integer), _

              CType(TOKEN_TYPE.TokenPrimary, Integer), primaryToken)

            If retVal = False Then

                ErrStr = String.Format("DuplicateTokenEx Error:" & _

                  "{0}", Marshal.GetLastWin32Error)
 

                'Debug.WriteLine(message)

            End If

        Else

            ErrStr = String.Format("OpenProcessToken Error:" & _

              "{0}", Marshal.GetLastWin32Error)

            'Debug.WriteLine(message)

        End If

        Return primaryToken

    End Function
 

    Private Shared Function GetEnvironmentBlock(ByVal token As IntPtr) As IntPtr

        Dim envBlock As IntPtr = IntPtr.Zero

        Dim retVal As Boolean = CreateEnvironmentBlock(envBlock, token, False)

        If retVal = False Then

            Dim message As String = String.Format("CreateEnvironmentBlock" & _

              "Error: {0}", Marshal.GetLastWin32Error)

            ' Debug.WriteLine(message)

        End If

        Return envBlock

    End Function
 

    Public Shared Function Launch(ByVal appCmdLine As String) As Boolean

        Dim ret As Boolean = False

        Dim ps As Process() = Process.GetProcessesByName("explorer")

        Dim processId As Integer = -1

        If ps.Length > 0 Then

            processId = ps(0).Id

        End If

        If processId > 1 Then

            Dim token As IntPtr = GetPrimaryToken(processId)

            If Not (token.Equals(IntPtr.Zero)) Then

                Dim envBlock As IntPtr = GetEnvironmentBlock(token)

                ret = LaunchProcessAsUser(appCmdLine, token, envBlock)

                If Not (envBlock.Equals(IntPtr.Zero)) Then

                    DestroyEnvironmentBlock(envBlock)

                End If

            End If

        End If

        Return ret

    End Function

    Public Shared Function GetUserToken() As IntPtr

        ErrStr = ""

        Dim token As IntPtr

        Dim ps As Process() = Process.GetProcessesByName("explorer")

        Dim processId As Integer = -1

        If ps.Length > 0 Then

            processId = ps(0).Id

        End If

        If processId > 1 Then

            token = GetPrimaryToken(processId)
 

        End If
 

        Return token

    End Function
 

    Public Sub New()

        MyBase.New()

    End Sub

End Class

Open in new window

0
 
LVL 1

Author Comment

by:wint100
Comment Utility
This doesn't really help i'm afraid. Is there a way to just run the impersonate code without sending the fixed user, pass, domain and instead just send the credentials of the logged in user?
0
 
LVL 2

Expert Comment

by:Wegelin
Comment Utility

 My Code Works only  under the Computer where the Service is running.


With this Class you NOT need a USER or Domain Or Password!!!

With ProcessAsUser.GetUserToken you became the Token from The Logged In USER

So you can now Write your code so for printing in A Service with the logged in USER

in VB.NET

This code is as System

console.writeline("I Am SYSTEM")


 Dim impctx As WindowsImpersonationContext = WindowsIdentity.Impersonate(ProcessAsUser.GetUserToken)

           console.writeline("I Am USER")

            Dim pd As New System.Drawing.Printing.PrintDocument
            pd.DocumentName = "c:\temp\test.txt"
            pd.Print()

            impctx.Undo()

  console.writeline("I Am SYSTEM")












0
 
LVL 1

Author Comment

by:wint100
Comment Utility
OK, I'll give it a go and post back.

Do I just need to use the code in your original post???
0
 
LVL 2

Accepted Solution

by:
Wegelin earned 500 total points
Comment Utility
you only need to convert the ProccessAsUser Class to C#

and then do this

WindowsImpersonationContext impctx = WindowsIdentity.Impersonate(ProcessAsUser.GetUserToken);
System.Drawing.Printing.PrintDocument pd = new System.Drawing.Printing.PrintDocument();
pd.DocumentName = "c:\\temp\\test.txt";
pd.Print();
impctx.Undo();
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Recently while returning home from work my wife (another .NET developer) was murmuring something. On further poking she said that she has been assigned a task where she has to serialize and deserialize objects and she is afraid of serialization. Wha…
In my previous two articles we discussed Binary Serialization (http://www.experts-exchange.com/A_4362.html) and XML Serialization (http://www.experts-exchange.com/A_4425.html). In this article we will try to know more about SOAP (Simple Object Acces…
This tutorial demonstrates a quick way of adding group price to multiple Magento products.
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now