using latest build, trying to impersonate shadowing + other issue

134 views
Skip to first unread message

Robert Gijsen

unread,
Jan 5, 2012, 7:51:52 AM1/5/12
to Cassia Users
Hi,
I am not sure if I am allowed to ask questions here about the latest
available build (Cassia-2.1.0.119). I took that as I wrote a helpdesk-
assist tool using Cassia, and there is very strong demand for
shadowing sessions. I am using vb.net 2010.
session.StartRemoteControl(ConsoleKey.Multiply,
RemoteControlHotkeyModifiers.Control) works fine when I am running it
as a user with permissions to remote control the session. However, in
the rest of my tool I use impersonation of an elevated account, so the
helpdesk-user does not have to have all permissions on their own
account. This works fine for eveything except for shadowing. No error,
nothing happens either. I noticed the same if I use runas /user:<admin
user> "shadow.exe <session>", also nothing happens. Anyone have an
idea how to shadow using impersonation?


Another issue I have with 2.1 is that as soon as a serverconnection is
closed I loose some info in some variables I set. For example I have
this (not so smart) code:

TSServer = TSManager.GetRemoteServer(<servername>)
TSServer.Open()
Counter=0
For Each session As ITerminalServicesSession In
TSServer.GetSessions
CopyOfTSSessions (counter) = session
Counter = Counter + 1
Next
TSServer.Close()

Now, BEFORE the TSServer.Close() the CopyOfTSSessions array holds all
the sessions for that server. I need the session(x).UserName for
example, and it's there. However, AFTER TSServer.Close, even the
previously filled CopyOfTSSessions array does not contain most values
anymore, but says the connection to the server is not opened anymore.
Cassia 2.0 (the latest official) does not have this issue. Ofcourse I
don't have 'live' data in the CopyOfTSSessions array, but after
closing the server connection it retains the sessioninfo without
having to look it up to a connected server. Is there a way to get
Cassia 2.1 to behave the same?

Thanks in advance,
Robert

Dan Ports

unread,
Jan 5, 2012, 1:17:33 PM1/5/12
to cassia...@googlegroups.com
Robert,

Sure, questions about trunk builds are welcome. I'd prefer to get questions about 2.1 now rather than after officially releasing it!

Interesting that remote control wouldn't work while impersonating. How exactly are you impersonating the user? Can you share the relevant code snippet?

The difference you are seeing between version 2.0 and the trunk build is a result of the changes made for issue 24. In 2.0, we automatically fetched certain frequently used session properties (like UserName), but on trunk we don't eager load any properties. So if you want to make sure the user name is available after disconnecting from the server, you would need to do something like this:


               For Each session As ITerminalServicesSession In TSServer.GetSessions
                    Junk = session.UserName
               Next

Accessing a property will cause Cassia to load it and cache its value. You can write similar code to cache the values of any other properties you might need.

Dan


--
You received this message because you are subscribed to the Google Groups "Cassia Users" group.
To post to this group, send email to cassia...@googlegroups.com.
To unsubscribe from this group, send email to cassia-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/cassia-users?hl=en.


Robert Gijsen

unread,
Jan 5, 2012, 2:55:42 PM1/5/12
to Cassia Users
Wow just using a dummy to poll the property works perfectly! Thanks so
much for that.

Which brings me to the shadowing. I am not a pro-programmer, but I can
find my way in scripting quite well, which is how I ended up using
VB.NET. For the impersonation I found this code:
http://weblogs.asp.net/ralfw/archive/2003/11/24/39479.aspx. That seems
not to work very well. When I check UserPrincipal.Current.DisplayName
it actually shows the impersonated user, but shadowing just doesn't do
anything. I just found out that killing processes doesn't work either.
That does work though when I run the tool using the user I am actually
impersonating. I am starting to think the impersonation code or way I
use it is not OK.

Dan Ports

unread,
Jan 5, 2012, 3:40:53 PM1/5/12
to cassia...@googlegroups.com
Here's another impersonation sample you might consider:
http://code.google.com/p/cassia/issues/attachmentText?id=34&aid=8970215639575135171&name=Program.cs&token=PW4v2Hc0j6ffp5dsaLfiIUSq_ZU%3A1325795721222

It looks pretty similar to the code you referenced, though it passes NewCredentials to LogonUser rather than Interactive. My feeling is that the LogonType may make a difference here, but I am not an impersonation expert and haven't tried the scenario you described yet.

Is the remote machine in the same domain as the machine running the Cassia code?

Dan

Robert Gijsen

unread,
Jan 5, 2012, 4:11:04 PM1/5/12
to cassia...@googlegroups.com
I'm not an impersonation expert either, not al all. This code is written in C or something though, I'll look for a way to do this in VB.NET. I'll try to find some sample incorporating the NewCredentials passing, if I cannot find I'll adapt my current code. Thanks so far, I will update as soon as I have one.

Regards,
Robert

Dan Ports

unread,
Jan 5, 2012, 5:52:39 PM1/5/12
to cassia...@googlegroups.com
Also, some additional questions:
  • Are you calling StartRemoteControl from a process in a Remote Desktop session?
  • What Windows version is the remote server running (the server on which the session you are trying to shadow is running)?
  • What Windows version is running on the server where the Cassia code is executing?

I have to say, with recent RD versions, shadowing has become more of a second-class citizen. You can't shadow a RemoteApp session or a session using the true multiple monitor support added in RDP 7. Just something you may want to keep in mind.

Dan

Robert Gijsen

unread,
Jan 6, 2012, 2:56:02 AM1/6/12
to cassia...@googlegroups.com

We are running a mixture of 2008 and 2008R2 RDS servers. I am actually in the process of moving all to R2. I know of the limitations of RDP. We always start shadow sessions from within a RDP session, we can't do otherwise as our servers are in a remote datacenter and the helpdesk does not have VPN. That works fine though, except for when impersonating. The strange thing is that I can use shadow.exe file when ran under a user with remote-permissions, but not when I have a normal user 'runas' shadow.exe as the user with permissions. Gives me access denied. While I am typing I am thinking of UAC, which is diabled by GPO's but I will also double-check.

Robert

Robert Gijsen

unread,
Jan 11, 2012, 12:17:51 PM1/11/12
to Cassia Users
Yes, setting the LogonType to NewCredentials actually seems to make a
difference, it works now, using the code I already used (just altering
the login type from 2 to 9). Thanks for that!

One issue resides now, which is that when I use the shadowfunction
from within VB.NET 2010 (express by the way) as soon as the shadow is
started (even before the popup shows on the client-computer) the
VB.NET box bluescreens. The compiled version runs fine though on the
very same machine. I did a complete re-install of my development box,
with Windows 2008R2 SP1, same results. Does that sound familar?

Robert Gijsen

unread,
Jan 12, 2012, 10:42:06 AM1/12/12
to Cassia Users
Hi, I found another issue, which also relates to the impersonation
issues I had. Impersonation works fine now, when I am connecting to a
remote server. I can send messages, shadow, kill processes and logoff
users on behalf of my impersonated user. Great! Almost there!
However, when the user I want to control happens to be on the same
terminal server my tool is running, cassia doesn't want to work with
impersonation. It just fires the controls I send as my 'own' user, and
hence I get access denied errors. Now I open the connection to
Terminal Servers with .GetRemoteServer. Also when it's actually the
local server. So I also tried with .GetLocalServer, and found exactly
the same behaviour.

Is cassia supposed to behave different on a local server?

Dan Ports

unread,
Jan 12, 2012, 12:56:51 PM1/12/12
to cassia...@googlegroups.com
Robert,

This is probably related to the impersonation code you are using. Can you share the impersonation-related code you are using now?

Dan

Robert Gijsen

unread,
Jan 12, 2012, 1:03:57 PM1/12/12
to Cassia Users
I tried with two 'versions' of impersonation (both found from
internet). Both do the same. Sorry for the long reply here. I've split
the two different types I tried with a long line. I am currently on
the upper one.

Public Class AliasAccount
Private _username, _password, _domainname As String

Private _tokenHandle As New IntPtr(0)
Private _dupeTokenHandle As New IntPtr(0)
Private _impersonatedUser As
System.Security.Principal.WindowsImpersonationContext


Public Sub New(ByVal username As String, ByVal password As String)
Dim nameparts() As String = username.Split("\")
If nameparts.Length > 1 Then
_domainname = nameparts(0)
_username = nameparts(1)
Else
_username = username
End If
_password = password
End Sub

Public Sub New(ByVal username As String, ByVal password As String,
ByVal domainname As String)
_username = username
_password = password
_domainname = domainname
End Sub

' <summary>
' This logon type is intended for users who will be interactively
' using the computer, such as a user being logged on by a
' terminal server, remote shell, or similar process.
' This logon type has the additional expense of caching logon
' information for disconnected operations; therefore, it is
' inappropriate for some client/server applications, such as a
' mail server.
' </summary>
' Interactive = 2,

' <summary>
' This logon type is intended for high performance servers to
' authenticate plaintext passwords.
' The LogonUser function does not cache credentials for this
' logon type.
' </summary>
' Network = 3,

' <summary>
' This logon type is intended for batch servers, where processes
' may be executing on behalf of a user without their direct
' intervention. This type is also for higher performance servers
' that process many plaintext authentication attempts at a time,
' such as mail or Web servers.
' The LogonUser function does not cache credentials for this
' logon type.
' </summary>
' Batch = 4,

' <summary>
' Indicates a service-type logon. The account provided must have
' the service privilege enabled.
' </summary>
' Service = 5,

' <summary>
' This logon type is for GINA DLLs that log on users who will be
' interactively using the computer.
' This logon type can generate a unique audit record that shows
' when the workstation was unlocked.
' </summary>
' Unlock = 7,

' <summary>
' This logon type preserves the name and password in the
' authentication package, which allows the server to make
' connections to other network servers while impersonating the
' client. A server can accept plaintext credentials from a
' client, call LogonUser, verify that the user can access the
' system across the network, and still communicate with other
' servers.
' NOTE: Windows NT: This value is not supported.
' </summary>
' NetworkCleartext = 8,

' <summary>
' This logon type allows the caller to clone its current token
' and specify new credentials for outbound connections. The new
' logon session has the same local identifier but uses different
' credentials for other network connections.
' NOTE: This logon type is supported only by the
' LOGON32_PROVIDER_WINNT50 logon provider.
' NOTE: Windows NT: This value is not supported.
' </summary>
' NewCredentials = 9

Public Sub BeginImpersonation()
Const LOGON32_PROVIDER_DEFAULT As Integer = 0
Const LOGONTYPE As Integer = 9
Const SecurityImpersonation As Integer = 2

Dim win32ErrorNumber As Integer

_tokenHandle = IntPtr.Zero
_dupeTokenHandle = IntPtr.Zero

If Not LogonUser(_username, _domainname, _password, LOGONTYPE,
LOGON32_PROVIDER_DEFAULT, _tokenHandle) Then
win32ErrorNumber =
System.Runtime.InteropServices.Marshal.GetLastWin32Error()
Throw New ImpersonationException(win32ErrorNumber,
GetErrorMessage(win32ErrorNumber), _username, _domainname)
End If

If Not DuplicateToken(_tokenHandle, SecurityImpersonation,
_dupeTokenHandle) Then
win32ErrorNumber =
System.Runtime.InteropServices.Marshal.GetLastWin32Error()

CloseHandle(_tokenHandle)
Throw New ImpersonationException(win32ErrorNumber, "Unable
to duplicate token!", _username, _domainname)
End If

Dim newId As New
System.Security.Principal.WindowsIdentity(_dupeTokenHandle)
_impersonatedUser = newId.Impersonate()
End Sub


Public Sub EndImpersonation()
If Not _impersonatedUser Is Nothing Then
_impersonatedUser.Undo()
_impersonatedUser = Nothing

If Not System.IntPtr.op_Equality(_tokenHandle,
IntPtr.Zero) Then
CloseHandle(_tokenHandle)
End If
If Not System.IntPtr.op_Equality(_dupeTokenHandle,
IntPtr.Zero) Then
CloseHandle(_dupeTokenHandle)
End If
End If
End Sub


Public ReadOnly Property username() As String
Get
Return _username
End Get
End Property

Public ReadOnly Property domainname() As String
Get
Return _domainname
End Get
End Property


Public ReadOnly Property currentWindowsUsername() As String
Get
Return
System.Security.Principal.WindowsIdentity.GetCurrent().Name
End Get
End Property


#Region "Exception Class"
Public Class ImpersonationException
Inherits System.Exception

Public ReadOnly win32ErrorNumber As Integer

Public Sub New(ByVal win32ErrorNumber As Integer, ByVal msg As
String, ByVal username As String, ByVal domainname As String)
MyBase.New(String.Format("Impersonation of {1}\{0} failed!
[{2}] {3}", username, domainname, win32ErrorNumber, msg))
Me.win32ErrorNumber = win32ErrorNumber
End Sub
End Class
#End Region


#Region "External Declarations and Helpers"
Private Declare Auto Function LogonUser Lib "advapi32.dll" (ByVal
lpszUsername As [String], _
ByVal lpszDomain As [String], ByVal lpszPassword As
[String], _
ByVal dwLogonType As Integer, ByVal dwLogonProvider As
Integer, _
ByRef phToken As IntPtr) As Boolean


Private Declare Auto Function DuplicateToken Lib
"advapi32.dll" (ByVal ExistingTokenHandle As IntPtr, _
ByVal SECURITY_IMPERSONATION_LEVEL As Integer, _
ByRef DuplicateTokenHandle As IntPtr) As Boolean


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


<System.Runtime.InteropServices.DllImport("kernel32.dll")> _
Private Shared Function FormatMessage(ByVal dwFlags As Integer,
ByRef lpSource As IntPtr, _
ByVal dwMessageId As Integer, ByVal dwLanguageId As
Integer, ByRef lpBuffer As [String], _
ByVal nSize As Integer, ByRef Arguments As IntPtr) As
Integer
End Function


Private Function GetErrorMessage(ByVal errorCode As Integer) As
String
Dim FORMAT_MESSAGE_ALLOCATE_BUFFER As Integer = &H100
Dim FORMAT_MESSAGE_IGNORE_INSERTS As Integer = &H200
Dim FORMAT_MESSAGE_FROM_SYSTEM As Integer = &H1000

Dim messageSize As Integer = 255
Dim lpMsgBuf As String = Nothing
Dim dwFlags As Integer = FORMAT_MESSAGE_ALLOCATE_BUFFER Or
FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS

Dim ptrlpSource As IntPtr = IntPtr.Zero
Dim prtArguments As IntPtr = IntPtr.Zero

Dim retVal As Integer = FormatMessage(dwFlags, ptrlpSource,
errorCode, 0, lpMsgBuf, messageSize, prtArguments)
If 0 = retVal Then
Throw New System.Exception("Failed to format message for
error code " + errorCode.ToString() + ". ")
End If

Return lpMsgBuf
End Function

#End Region

End Class



-------------------------------------------------------------------------------------------------------------------------------------------------------------
second version below
-------------------------------------------------------------------------------------------------------------------------------------------------------------

Imports System.Threading
Imports System.Configuration
Imports SR = System.Reflection
Imports System.Text
Imports System.Security.Principal

Public Class Utilities

Private Const LOGON32_PROVIDER_DEFAULT As Integer = 0
Private Const LOGON32_LOGON_INTERACTIVE As Integer = 2
Private Const LOGON32_LOGON_NETWORK As Integer = 3
Private Const LOGON32_LOGON_BATCH As Integer = 4
Private Const LOGON32_LOGON_SERVICE As Integer = 5
Private Const LOGON32_LOGON_UNLOCK As Integer = 7
Private Const LOGON32_LOGON_NETWORK_CLEARTEXT As Integer = 8
Private Const LOGON32_LOGON_NEW_CREDENTIALS As Integer = 9

Private Shared ImpersonationContext As WindowsImpersonationContext

Declare Function LogonUserA Lib "advapi32.dll" ( _
ByVal lpszUsername As String, _
ByVal lpszDomain As String, _
ByVal lpszPassword As String, _
ByVal dwLogonType As Integer, _
ByVal dwLogonProvider As Integer, _
ByRef phToken As IntPtr) As Integer

Declare Auto Function DuplicateToken Lib "advapi32.dll" ( _
ByVal ExistingTokenHandle As IntPtr, _
ByVal ImpersonationLevel As Integer, _
ByRef DuplicateTokenHandle As IntPtr) As
Integer
Declare Auto Function RevertToSelf Lib "advapi32.dll" () As Long
Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle
As IntPtr) As Long


' NOTE:
' The identity of the process that impersonates a specific user on
a thread must have
' "Act as part of the operating system" privilege. If the the
Aspnet_wp.exe process runs
' under a the ASPNET account, this account does not have the
required privileges to
' impersonate a specific user. This information applies only to
the .NET Framework 1.0.
' This privilege is not required for the .NET Framework 1.1.
'
' Sample call:
'
' If impersonateValidUser("username", "domain", "password")
Then
' 'Insert your code here.
'
' undoImpersonation()
' Else
' 'Impersonation failed. Include a fail-safe mechanism
here.
' End If
'
Public Shared Function ImpersonateValidUser(ByVal strUserName As
String, _
ByVal strDomain As String, ByVal strPassword As String) As
Boolean
Dim token As IntPtr = IntPtr.Zero
Dim tokenDuplicate As IntPtr = IntPtr.Zero
Dim tempWindowsIdentity As WindowsIdentity

ImpersonateValidUser = False

If RevertToSelf() <> 0 Then
If LogonUserA(strUserName, strDomain, _
strPassword, _
LOGON32_LOGON_NEW_CREDENTIALS, _
LOGON32_PROVIDER_DEFAULT, token) <> 0 Then
If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then
tempWindowsIdentity = New
WindowsIdentity(tokenDuplicate)
ImpersonationContext =
tempWindowsIdentity.Impersonate()

If Not (ImpersonationContext Is Nothing) Then
ImpersonateValidUser = True
End If
End If
End If
End If

If Not tokenDuplicate.Equals(IntPtr.Zero) Then
CloseHandle(tokenDuplicate)
End If

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

End Function

Public Shared Sub UndoImpersonation()

ImpersonationContext.Undo()

End Sub
End Class

Dan Ports

unread,
Jan 12, 2012, 1:07:43 PM1/12/12
to cassia...@googlegroups.com
Hm, I think the NewCredentials logon type will not work for you with the local server: "The new logon session has the same local identifier but uses different credentials for other network connections." Perhaps you'd want to try the Network/Batch/NetworkCleartext options.

Dan

Robert Gijsen

unread,
Jan 13, 2012, 12:14:25 PM1/13/12
to Cassia Users
Just tried all of them, no positive results. Does the impersonation
code you gave me,
http://www.google.com/url?sa=D&q=http://code.google.com/p/cassia/issues/attachmentText%3Fid%3D34%26aid%3D8970215639575135171%26name%3DProgram.cs%26token%3DPW4v2Hc0j6ffp5dsaLfiIUSq_ZU%253A1325795721222&usg=AFQjCNG6qsSfApd9WNTq88IHUOmqfV8TQA,
actually work when running on the same machine?

I don't really understand the problem by the way, you'd normally use
impersonation mostly to the local machine right? In scripts or other
tools. I just can't get my thoughts right about this one.

Dan Ports

unread,
Jan 13, 2012, 12:17:12 PM1/13/12
to cassia...@googlegroups.com
I haven't tried running any of the impersonation code myself yet. Does shadow work with runas on the local machine?

Dan

Robert Gijsen

unread,
Jan 13, 2012, 12:46:59 PM1/13/12
to Cassia Users
I just tried runas shadow.exe (the windows-tool) which does not work.
Then I removed the impersonation-bit from a test-app I have which just
remotes a session on the same server, using runas it gives me access
denied on that. It seems somehow it is just not supported but I just
cannot believe that :)

Dan Ports

unread,
Jan 14, 2012, 8:15:19 PM1/14/12
to cassia...@googlegroups.com
Hm, it must have something to do with how the Windows internals work. If I find any information on this, I will pass it along.

Dan

Robert Gijsen

unread,
Feb 8, 2012, 11:12:13 AM2/8/12
to Cassia Users
Not wanting to push anybody, but have by any change had time to look
into this?

Best regards, Robert

Dan Ports

unread,
Feb 8, 2012, 10:11:20 PM2/8/12
to cassia...@googlegroups.com
Well, I was able to reproduce it just now, but that's about all -- if I think of something else to try, I'll let you know.

Dan

Dan Ports

unread,
Feb 16, 2012, 9:23:17 PM2/16/12
to cassia...@googlegroups.com
After thinking about this a bit more, my feeling is that if runas shadow doesn't work (it didn't work for me either -- or for this newsgroup user), this scenario isn't supported. I wouldn't rule out the possibility of low-level debugging turning up some useful leads, but at the very least, the problem is out of Cassia's scope.

If you'd like to pursue this further, you might want to give the resources mentioned in this blog post a shot.

Also, is it an option to simply give the relevant users Remote Control permissions on the appropriate servers? That would be the easiest solution.

Dan

Robert Gijsen

unread,
Feb 17, 2012, 3:36:11 AM2/17/12
to Cassia Users
Hi Dan,
thanks for your input. I already figured that it might be a no-go,
although I still think it's a bit weird why it wouldn't be able to
work or wouldn't be allowed by MS. At this time, indeed the 'helpdesk'
users have remote control permissions on the servers. What we do is
'rent out' shared Terminal Servers for small companies, using App-V
and some other technologies. We have about 60 users per TS but they
might very well be all from different companies / customers. The tool
I wrote filters out everything they are not allowed to do, but giving
them remote-control to the helpdesk user will not actually revoke the
permission to remote-control someone of another company. I've made a
lot of check in my tool to verify a user isn't remoting someone he is
not allowed to but it just doesn't feel right to just give them all
permissions.
I've been thinking about this, and what I think I'm gonna do is put up
another small TS dedicated to this tool, and use RemoteApp to get it
'seamless' to the helpdesk-users.

Thanks a lot for your help.

Regards,
Robert

On Feb 17, 3:23 am, Dan Ports <danpo...@gmail.com> wrote:
> After thinking about this a bit more, my feeling is that if runas shadow
> doesn't work (it didn't work for me either -- or for this newsgroup
> user<http://forums.msterminalservices.org/tsadmin-shadow-runas-ftopict1575...>),
> this scenario isn't supported. I wouldn't rule out the possibility of
> low-level debugging turning up some useful leads, but at the very least,
> the problem is out of Cassia's scope.
>
> If you'd like to pursue this further, you might want to give the resources
> mentioned in this blog
> post<http://blogs.msdn.com/b/rds/archive/2011/11/21/help-finding-remote-de...>
> a
> shot.
>
> Also, is it an option to simply give the relevant users Remote Control
> permissions<http://msdn.microsoft.com/en-us/library/windows/desktop/aa383488(v=vs...>on
> the appropriate servers? That would be the easiest solution.
>
> Dan
>
>
>
>
>
>
>
> On Wed, Feb 8, 2012 at 10:11 PM, Dan Ports <danpo...@gmail.com> wrote:
> > Well, I was able to reproduce it just now, but that's about all -- if I
> > think of something else to try, I'll let you know.
>
> > Dan
>

Dan Ports

unread,
Feb 23, 2012, 8:52:15 PM2/23/12
to cassia...@googlegroups.com
No problem. I'm not sure why runas shadow doesn't work -- it must have something to do with the Windows plumbing, and I haven't tried to step into the Win32 API calls.

I'm not sure whether you can shadow a normal RD session from a RemoteApp session, if that's what you'd be trying to do by setting up a separate server...

Dan

Robert Gijsen

unread,
Feb 24, 2012, 2:50:52 AM2/24/12
to cassia...@googlegroups.com

I can already tell that doesnt work.. probably because there is no fixed resolution in a remote-app or so. So I still havent found a solution.

Sent from my HTC

Dan Ports

unread,
Mar 1, 2012, 8:34:35 PM3/1/12
to cassia...@googlegroups.com
Yeah, I understand your general security concern. Practically speaking, though, it shouldn't matter if there's no way for users to initiate shadowing except through your application, assuming you're not allowing users to connect to the RPC port...which I'm sure you wouldn't be.

If it were possible to set the security descriptor for an individual RD session, that might also solve your issue...but I'm not aware of any (documented or undocumented) way to do that, unfortunately.

Dan
Reply all
Reply to author
Forward
0 new messages