Skip to content

Commit

Permalink
Merge pull request #267 from dotnet/feature/sspi-session-key
Browse files Browse the repository at this point in the history
Expose session key to context caller
  • Loading branch information
SteveSyfuhs authored Nov 16, 2021
2 parents 5dc8ed8 + 55671d3 commit ddc0f27
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 23 deletions.
18 changes: 16 additions & 2 deletions Kerberos.NET/Win32/NativeMethods.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// Licensed to The .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// -----------------------------------------------------------------------
Expand Down Expand Up @@ -112,6 +112,13 @@ internal static extern SecStatus QueryContextAttributesString(
ref SecPkgContext_SecString pBuffer
);

[DllImport(SECUR32, SetLastError = true, EntryPoint = "QueryContextAttributes", CharSet = CharSet.Unicode)]
internal static extern SecStatus QueryContextAttributesSession(
ref SECURITY_HANDLE phContext,
SecurityContextAttribute ulAttribute,
ref SecPkgContext_SessionKey pBuffer
);

[DllImport(SECUR32)]
internal static extern uint FreeCredentialsHandle(SECURITY_HANDLE* handle);

Expand Down Expand Up @@ -544,5 +551,12 @@ internal struct SecPkgContext_SecString
{
public void* sValue;
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct SecPkgContext_SessionKey
{
public uint SessionKeyLength;
public void* SessionKey;
}
}
}
}
5 changes: 3 additions & 2 deletions Kerberos.NET/Win32/SecurityContextAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// Licensed to The .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// -----------------------------------------------------------------------
Expand All @@ -13,11 +13,12 @@ public enum SecurityContextAttribute
SECPKG_ATTR_DCE_INFO = 3,
SECPKG_ATTR_STREAM_SIZES = 4,
SECPKG_ATTR_AUTHORITY = 6,
SECPKG_ATTR_SESSION_KEY = 9,
SECPKG_ATTR_PACKAGE_INFO = 10,
SECPKG_ATTR_NEGOTIATION_INFO = 12,
SECPKG_ATTR_UNIQUE_BINDINGS = 25,
SECPKG_ATTR_ENDPOINT_BINDINGS = 26,
SECPKG_ATTR_CLIENT_SPECIFIED_TARGET = 27,
SECPKG_ATTR_APPLICATION_PROTOCOL = 35
}
}
}
10 changes: 6 additions & 4 deletions Kerberos.NET/Win32/SspiContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// Licensed to The .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// -----------------------------------------------------------------------
Expand All @@ -23,9 +23,11 @@ public SspiContext(string spn, string package = "Kerberos")
this.context = new SspiSecurityContext(Credential.Current(), package);
}

public byte[] RequestToken()
public byte[] SessionKey => this.context.QueryContextAttributeSession();

public byte[] RequestToken(byte[] serverResponse = null)
{
var status = this.context.InitializeSecurityContext(this.spn, null, out byte[] clientRequest);
var status = this.context.InitializeSecurityContext(this.spn, serverResponse, out byte[] clientRequest);

if (status == ContextStatus.Error)
{
Expand Down Expand Up @@ -64,4 +66,4 @@ public void Dispose()
GC.SuppressFinalize(this);
}
}
}
}
39 changes: 37 additions & 2 deletions Kerberos.NET/Win32/SspiSecurityContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// Licensed to The .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// -----------------------------------------------------------------------
Expand Down Expand Up @@ -104,6 +104,41 @@ public unsafe string QueryContextAttributeAsString(SecurityContextAttribute attr
return strValue;
}

public unsafe byte[] QueryContextAttributeSession()
{
SecPkgContext_SessionKey pBuffer = default;
SecStatus status;
byte[] bytes = null;

RuntimeHelpers.PrepareConstrainedRegions();
try
{
}
finally
{
try
{
status = QueryContextAttributesSession(ref this.securityContext, SecurityContextAttribute.SECPKG_ATTR_SESSION_KEY, ref pBuffer);

if (status == SecStatus.SEC_E_OK)
{
bytes = new Span<byte>(pBuffer.SessionKey, (int)pBuffer.SessionKeyLength).ToArray();
}
}
finally
{
ThrowIfError(FreeContextBuffer(pBuffer.SessionKey));
}
}

if (status != SecStatus.SEC_E_UNSUPPORTED_FUNCTION && status > SecStatus.SEC_E_ERROR)
{
throw new Win32Exception((int)status);
}

return bytes;
}

private static void ThrowIfError(uint result)
{
if (result != 0 && result != 0x80090301)
Expand Down Expand Up @@ -360,4 +395,4 @@ public unsafe void Dispose()
}
}
}
}
}
35 changes: 22 additions & 13 deletions Tests/Tests.Kerberos.NET/Win32/SspiTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
// -----------------------------------------------------------------------

using System;
using Kerberos.NET;
using Kerberos.NET.Crypto;
using Kerberos.NET.Entities;
using Kerberos.NET.Win32;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

namespace Tests.Kerberos.NET
{
Expand All @@ -17,27 +17,36 @@ public class SspiTests
[TestMethod]
public void TryGettingSspiTicketTest()
{
using (var contextSender = new SspiContext($"host/{Environment.MachineName}", "Negotiate"))
using (var contextReceiver = new SspiContext($"host/{Environment.MachineName}", "Negotiate"))
using (var contextSender = new SspiContext($"host/{Environment.MachineName}", "negotiate"))
using (var contextReceiver = new SspiContext($"host/{Environment.MachineName}", "negotiate"))
{
var token = contextSender.RequestToken();

Assert.IsNotNull(token);
byte[] token = null;
byte[] serverResponse = null;

var contextToken = MessageParser.Parse<NegotiateContextToken>(token);
do
{
token = contextSender.RequestToken(serverResponse);

Assert.IsNotNull(contextToken);
Assert.IsNotNull(token);

contextReceiver.AcceptToken(token, out byte[] serverResponse);

Assert.IsNotNull(serverResponse);
if (token != null && token.Length > 0)
{
contextReceiver.AcceptToken(token, out serverResponse);
Assert.IsNotNull(serverResponse);
}
}
while (token != null && token.Length > 0);

var serverContext = NegotiationToken.Decode(serverResponse);

Assert.IsNotNull(serverContext);
Assert.IsNotNull(serverContext.ResponseToken);
Assert.IsNull(serverContext.InitialToken);

Assert.IsNotNull(contextSender.SessionKey);

Assert.IsTrue(KerberosCryptoTransformer.AreEqualSlow(contextSender.SessionKey, contextReceiver.SessionKey));
}
}
}
}
}

0 comments on commit ddc0f27

Please sign in to comment.