Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4ac2501
WIP: Add implementation of NegotiateAuthentication
filipnavara Jun 3, 2022
c7a6bce
WIP: Update error code mapping
filipnavara Jun 4, 2022
5d5c61f
Spanify input of GetOutgoingBlob
filipnavara Jun 4, 2022
d0d1bba
Update comments
filipnavara Jun 4, 2022
982e7c3
Move NegotiateStreamPal.Encrypt/Decrypt to shared sources. Unix imple…
filipnavara Jun 4, 2022
cbd24a2
Revert accidental change
filipnavara Jun 4, 2022
9d4d557
Build fixes.
filipnavara Jun 4, 2022
c371515
Fix error handling condition
filipnavara Jun 4, 2022
3306c67
Update error mapping based on HttpListener usage.
filipnavara Jun 4, 2022
065d87c
WIP: HttpListener test
filipnavara Jun 7, 2022
447020a
Move workaround from HttpListener to low-level SSPI code
filipnavara Jun 7, 2022
bed08fa
Fix build
filipnavara Jun 14, 2022
318ca06
Clean up
filipnavara Jun 14, 2022
5474811
Revert "WIP: HttpListener test"
filipnavara Jun 14, 2022
4cdb8af
Convert System.Net.Http.FunctionalTests to use NegotiateAuthenticatio…
filipnavara Jun 14, 2022
34e47c4
Dispose the identity along NegotiateAuthentication
filipnavara Jun 14, 2022
73ac446
Modify unit tests to use the new API
filipnavara Jun 14, 2022
7e85f10
Add exceptions for invalid inputs/states
filipnavara Jun 14, 2022
7c870bd
Remove tvOS unsupported marker, managed NTLM is used on tvOS
filipnavara Jun 16, 2022
1bd0cde
Apply suggestions from code review
filipnavara Jun 14, 2022
e93f073
Fix typo
filipnavara Jun 14, 2022
5aae44d
Remove reference equality checks from IsNTLM/IsKerberos
filipnavara Jun 16, 2022
b746590
Remove NTAuthentication.AssociatedName to make it more obvious which …
filipnavara Jun 16, 2022
dd7dd9a
Add comment
filipnavara Jun 16, 2022
39a43b2
Add more tests, handle unsupported protocols
filipnavara Jun 16, 2022
9a4ccdf
Handle NotSupportedException from NTAuthentication constructor
filipnavara Jun 16, 2022
e86ce79
Add workaround for linker issue
filipnavara Jun 17, 2022
ffed0be
Apply suggestions from code review
filipnavara Jun 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Move NegotiateStreamPal.Encrypt/Decrypt to shared sources. Unix imple…
…mentation already had them and they get trimmed anyway.
  • Loading branch information
filipnavara authored Jun 16, 2022
commit 982e7c32297619acb0356515b94e5c7f4a988daf
Original file line number Diff line number Diff line change
Expand Up @@ -263,5 +263,201 @@ internal static int MakeSignature(SafeDeleteContext securityContext, byte[] buff
// return signed size
return securityBuffer[0].size + securityBuffer[1].size;
}

internal static int Encrypt(
SafeDeleteContext securityContext,
ReadOnlySpan<byte> buffer,
bool isConfidential,
bool isNtlm,
[NotNull] ref byte[]? output,
uint sequenceNumber)
{
SecPkgContext_Sizes sizes = default;
bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES, ref sizes);
Debug.Assert(success);

int maxCount = checked(int.MaxValue - 4 - sizes.cbBlockSize - sizes.cbSecurityTrailer);
if (buffer.Length > maxCount)
{
throw new ArgumentOutOfRangeException(nameof(buffer.Length), SR.Format(SR.net_io_out_range, maxCount));
}

int resultSize = buffer.Length + sizes.cbSecurityTrailer + sizes.cbBlockSize;
if (output == null || output.Length < resultSize + 4)
{
output = new byte[resultSize + 4];
}

// Make a copy of user data for in-place encryption.
buffer.CopyTo(output.AsSpan(4 + sizes.cbSecurityTrailer));

// Prepare buffers TOKEN(signature), DATA and Padding.
ThreeSecurityBuffers buffers = default;
var securityBuffer = MemoryMarshal.CreateSpan(ref buffers._item0, 3);
securityBuffer[0] = new SecurityBuffer(output, 4, sizes.cbSecurityTrailer, SecurityBufferType.SECBUFFER_TOKEN);
securityBuffer[1] = new SecurityBuffer(output, 4 + sizes.cbSecurityTrailer, buffer.Length, SecurityBufferType.SECBUFFER_DATA);
securityBuffer[2] = new SecurityBuffer(output, 4 + sizes.cbSecurityTrailer + buffer.Length, sizes.cbBlockSize, SecurityBufferType.SECBUFFER_PADDING);

int errorCode;
if (isConfidential)
{
errorCode = SSPIWrapper.EncryptMessage(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber);
}
else
{
if (isNtlm)
{
securityBuffer[1].type |= SecurityBufferType.SECBUFFER_READONLY;
}

errorCode = SSPIWrapper.MakeSignature(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, 0);
}

if (errorCode != 0)
{
Exception e = new Win32Exception(errorCode);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, e);
throw e;
}

// Compacting the result.
resultSize = securityBuffer[0].size;
bool forceCopy = false;
if (resultSize != sizes.cbSecurityTrailer)
{
forceCopy = true;
Buffer.BlockCopy(output, securityBuffer[1].offset, output, 4 + resultSize, securityBuffer[1].size);
}

resultSize += securityBuffer[1].size;
if (securityBuffer[2].size != 0 && (forceCopy || resultSize != (buffer.Length + sizes.cbSecurityTrailer)))
{
Buffer.BlockCopy(output, securityBuffer[2].offset, output, 4 + resultSize, securityBuffer[2].size);
}

resultSize += securityBuffer[2].size;
unchecked
{
output[0] = (byte)((resultSize) & 0xFF);
output[1] = (byte)(((resultSize) >> 8) & 0xFF);
output[2] = (byte)(((resultSize) >> 16) & 0xFF);
output[3] = (byte)(((resultSize) >> 24) & 0xFF);
}

return resultSize + 4;
}

internal static int Decrypt(
SafeDeleteContext securityContext,
byte[]? buffer,
int offset,
int count,
bool isConfidential,
bool isNtlm,
out int newOffset,
uint sequenceNumber)
{
if (offset < 0 || offset > (buffer == null ? 0 : buffer.Length))
{
Debug.Fail("Argument 'offset' out of range.");
throw new ArgumentOutOfRangeException(nameof(offset));
}

if (count < 0 || count > (buffer == null ? 0 : buffer.Length - offset))
{
Debug.Fail("Argument 'count' out of range.");
throw new ArgumentOutOfRangeException(nameof(count));
}

if (isNtlm)
{
return DecryptNtlm(securityContext, buffer, offset, count, isConfidential, out newOffset, sequenceNumber);
}

//
// Kerberos and up
//
TwoSecurityBuffers buffers = default;
var securityBuffer = MemoryMarshal.CreateSpan(ref buffers._item0, 2);
securityBuffer[0] = new SecurityBuffer(buffer, offset, count, SecurityBufferType.SECBUFFER_STREAM);
securityBuffer[1] = new SecurityBuffer(0, SecurityBufferType.SECBUFFER_DATA);

int errorCode;
if (isConfidential)
{
errorCode = SSPIWrapper.DecryptMessage(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber);
}
else
{
errorCode = SSPIWrapper.VerifySignature(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber);
}

if (errorCode != 0)
{
Exception e = new Win32Exception(errorCode);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, e);
throw e;
}

if (securityBuffer[1].type != SecurityBufferType.SECBUFFER_DATA)
{
throw new InternalException(securityBuffer[1].type);
}

newOffset = securityBuffer[1].offset;
return securityBuffer[1].size;
}

private static int DecryptNtlm(
SafeDeleteContext securityContext,
byte[]? buffer,
int offset,
int count,
bool isConfidential,
out int newOffset,
uint sequenceNumber)
{
const int ntlmSignatureLength = 16;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: NtlmSignatureLength

// For the most part the arguments are verified in Decrypt().
if (count < ntlmSignatureLength)
{
Debug.Fail("Argument 'count' out of range.");
throw new ArgumentOutOfRangeException(nameof(count));
}

TwoSecurityBuffers buffers = default;
var securityBuffer = MemoryMarshal.CreateSpan(ref buffers._item0, 2);
securityBuffer[0] = new SecurityBuffer(buffer, offset, ntlmSignatureLength, SecurityBufferType.SECBUFFER_TOKEN);
securityBuffer[1] = new SecurityBuffer(buffer, offset + ntlmSignatureLength, count - ntlmSignatureLength, SecurityBufferType.SECBUFFER_DATA);

int errorCode;
SecurityBufferType realDataType = SecurityBufferType.SECBUFFER_DATA;

if (isConfidential)
{
errorCode = SSPIWrapper.DecryptMessage(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber);
}
else
{
realDataType |= SecurityBufferType.SECBUFFER_READONLY;
securityBuffer[1].type = realDataType;
errorCode = SSPIWrapper.VerifySignature(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber);
}

if (errorCode != 0)
{
Exception e = new Win32Exception(errorCode);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, e);
throw new Win32Exception(errorCode);
}

if (securityBuffer[1].type != realDataType)
{
throw new InternalException(securityBuffer[1].type);
}

newOffset = securityBuffer[1].offset;
return securityBuffer[1].size;
}
}
}
Loading