So, the best solution we have so far is to use the token from the ServiceSecurityContext.Current.WindowsIdentity
and create a new primary token that we serialize and store for later. The downside is that the token is invalid once the service is restarted, but it's a fine temporary work-around until we can revise our service so that we don't require a persistent authorization from the user. We looked at using S4U2Proxy which would do what we want, but the requirement for configuring the domain account the service runs under is a bit onerous for our users. The working code snippet is below (note: we probably don't need to serialize any more, but it was easier to keep in place since we don't have to update our database schema. Also, we could break out the serialize routine from the token duplication to make the code more manageable):
Deserialize code:
''' <summary>
''' Deserializes a user token from a binary encoded byte array.
''' </summary>
''' <param name="identity">A byte array containing an binary representation of a user token.</param>
''' <returns>The deserialized user token from the byte array.</returns>
Private Function DeserializeWindowsIdentityToken(ByVal identity As Byte()) As IntPtr
If IsNothing(identity) Then Return Nothing
Dim stream As New MemoryStream(identity)
Dim serializer As New BinaryFormatter()
Try
Dim obj As Object = serializer.Deserialize(stream)
Return CType(obj, IntPtr)
Catch ex As Exception
Return IntPtr.Zero
End Try
End Function ' DeserializeWindowsIdentityToken
Serialize code:
''' <summary>
''' Serializes a user token as a binary encoded byte array.
''' </summary>
''' <param name="identity">The token to serialize.</param>
''' <returns>A byte array containing a binary representation of the token.</returns>
Private Function SerializeWindowsIdentityToken(ByVal identity As IntPtr) As Byte()
Try
Dim newToken As IntPtr = IntPtr.Zero
Const securityDelegation As Int16 = 3
Const tokenPrimary As Integer = 1
Const maximumAllowed As Integer = &H2000000
Dim sa As New SecurityAttributes()
sa.bInheritHandle = True
sa.Length = Marshal.SizeOf(sa)
sa.lpSecurityDescriptor = IntPtr.Zero
If DuplicateTokenEx(identity, maximumAllowed, sa, securityDelegation, tokenPrimary, newToken) = 0 Then Return Nothing
Dim streamWriter As New MemoryStream()
Dim serializer As New BinaryFormatter
serializer.Serialize(streamWriter, newToken)
Return streamWriter.ToArray()
Catch ex As Exception
Return Nothing
End Try
End Function ' SerializeWindowsIdentityToken
<StructLayout(LayoutKind.Sequential)>
Private Structure SecurityAttributes
Public Length As Integer
Public lpSecurityDescriptor As IntPtr
Public bInheritHandle As Boolean
End Structure ' SecurityAttributes
<DllImport("advapi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Private Shared Function DuplicateTokenEx(ByVal existingTokenHandle As IntPtr,
ByVal desiredAccess As UInteger,
ByRef threadAttributes As SecurityAttributes,
ByVal impersonationLevel As Integer,
ByVal tokenType As Integer,
ByRef duplicateTokenHandle As IntPtr) As Integer
End Function ' DuplicateTokenEx
Token capture:
Dim storedToken As Byte() = SerializeWindowsIdentityToken(ServiceSecurityContext.Current.WindowsIdentity.Token)
Usage:
Dim identity As IntPtr = DeserializeWindowsIdentityToken(storedToken)
Dim cxt As WindowsImpersonationContext = Nothing
If Not IsNothing(identity) AndAlso identity <> IntPtr.Zero Then
Try
Dim identity As New WindowsIdentity(identity)
cxt = identity.Impersonate()
Catch ex As Exception
' Perform error handling
End Try
End If
' Perform operations
If Not IsNothing(cxt) Then cxt.Dispose()