Skip to content

Commit

Permalink
Implement NegotiateAuthentication API (#70720)
Browse files Browse the repository at this point in the history
* WIP: Add implementation of NegotiateAuthentication

Switch System.Net.Http to use NegotiateAuthentication
Fix IsCompleted in managed NTLM implementation

* WIP: Update error code mapping

* Spanify input of GetOutgoingBlob

* Update comments

* Move NegotiateStreamPal.Encrypt/Decrypt to shared sources. Unix implementation already had them and they get trimmed anyway.

* Revert accidental change

* Build fixes.

* Fix error handling condition

* Update error mapping based on HttpListener usage.

* WIP: HttpListener test

* Move workaround from HttpListener to low-level SSPI code

* Fix build

* Clean up

* Revert "WIP: HttpListener test"

This reverts commit 18d7d93f04c93e048efcaca0f3c55c3f1f73516a.

* Convert System.Net.Http.FunctionalTests to use NegotiateAuthentication instead of NTAuthentication

* Dispose the identity along NegotiateAuthentication

* Modify unit tests to use the new API

* Add exceptions for invalid inputs/states

* Remove tvOS unsupported marker, managed NTLM is used on tvOS

* Apply suggestions from code review

Co-authored-by: Stephen Toub <[email protected]>

* Fix typo

* Remove reference equality checks from IsNTLM/IsKerberos

* Remove NTAuthentication.AssociatedName to make it more obvious which exceptions are thrown

* Add comment

* Add more tests, handle unsupported protocols

* Handle NotSupportedException from NTAuthentication constructor

* Add workaround for linker issue

* Apply suggestions from code review

Co-authored-by: Stephen Toub <[email protected]>
  • Loading branch information
filipnavara and stephentoub authored Jun 21, 2022
1 parent 3b8a6f4 commit 79f6709
Show file tree
Hide file tree
Showing 31 changed files with 1,294 additions and 1,020 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,21 @@ internal static partial Status ReleaseCred(
ref IntPtr credHandle);

[LibraryImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_InitSecContext")]
internal static partial Status InitSecContext(
private static partial Status InitSecContext(
out Status minorStatus,
SafeGssCredHandle initiatorCredHandle,
ref SafeGssContextHandle contextHandle,
[MarshalAs(UnmanagedType.Bool)] bool isNtlmOnly,
SafeGssNameHandle? targetName,
uint reqFlags,
byte[]? inputBytes,
ref byte inputBytes,
int inputLength,
ref GssBuffer token,
out uint retFlags,
[MarshalAs(UnmanagedType.Bool)] out bool isNtlmUsed);

[LibraryImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_InitSecContextEx")]
internal static partial Status InitSecContext(
private static partial Status InitSecContext(
out Status minorStatus,
SafeGssCredHandle initiatorCredHandle,
ref SafeGssContextHandle contextHandle,
Expand All @@ -96,23 +96,99 @@ internal static partial Status InitSecContext(
int cbtSize,
SafeGssNameHandle? targetName,
uint reqFlags,
byte[]? inputBytes,
ref byte inputBytes,
int inputLength,
ref GssBuffer token,
out uint retFlags,
[MarshalAs(UnmanagedType.Bool)] out bool isNtlmUsed);

internal static Status InitSecContext(
out Status minorStatus,
SafeGssCredHandle initiatorCredHandle,
ref SafeGssContextHandle contextHandle,
bool isNtlmOnly,
SafeGssNameHandle? targetName,
uint reqFlags,
ReadOnlySpan<byte> inputBytes,
ref GssBuffer token,
out uint retFlags,
out bool isNtlmUsed)
{
return InitSecContext(
out minorStatus,
initiatorCredHandle,
ref contextHandle,
isNtlmOnly,
targetName,
reqFlags,
ref MemoryMarshal.GetReference(inputBytes),
inputBytes.Length,
ref token,
out retFlags,
out isNtlmUsed);
}

internal static Status InitSecContext(
out Status minorStatus,
SafeGssCredHandle initiatorCredHandle,
ref SafeGssContextHandle contextHandle,
bool isNtlmOnly,
IntPtr cbt,
int cbtSize,
SafeGssNameHandle? targetName,
uint reqFlags,
ReadOnlySpan<byte> inputBytes,
ref GssBuffer token,
out uint retFlags,
out bool isNtlmUsed)
{
return InitSecContext(
out minorStatus,
initiatorCredHandle,
ref contextHandle,
isNtlmOnly,
cbt,
cbtSize,
targetName,
reqFlags,
ref MemoryMarshal.GetReference(inputBytes),
inputBytes.Length,
ref token,
out retFlags,
out isNtlmUsed);
}

[LibraryImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_AcceptSecContext")]
internal static partial Status AcceptSecContext(
private static partial Status AcceptSecContext(
out Status minorStatus,
SafeGssCredHandle acceptorCredHandle,
ref SafeGssContextHandle acceptContextHandle,
byte[]? inputBytes,
ref byte inputBytes,
int inputLength,
ref GssBuffer token,
out uint retFlags,
[MarshalAs(UnmanagedType.Bool)] out bool isNtlmUsed);

internal static Status AcceptSecContext(
out Status minorStatus,
SafeGssCredHandle acceptorCredHandle,
ref SafeGssContextHandle acceptContextHandle,
ReadOnlySpan<byte> inputBytes,
ref GssBuffer token,
out uint retFlags,
out bool isNtlmUsed)
{
return AcceptSecContext(
out minorStatus,
acceptorCredHandle,
ref acceptContextHandle,
ref MemoryMarshal.GetReference(inputBytes),
inputBytes.Length,
ref token,
out retFlags,
out isNtlmUsed);
}

[LibraryImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_DeleteSecContext")]
internal static partial Status DeleteSecContext(
out Status minorStatus,
Expand Down

This file was deleted.

This file was deleted.

49 changes: 42 additions & 7 deletions src/libraries/Common/src/System/Net/NTAuthentication.Common.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Net.Security;
Expand All @@ -10,7 +11,6 @@

namespace System.Net
{
[UnsupportedOSPlatform("tvos")]
internal sealed partial class NTAuthentication
{
private bool _isServer;
Expand Down Expand Up @@ -81,12 +81,17 @@ internal bool IsKerberos
{
get
{
if (_lastProtocolName == null)
{
_lastProtocolName = ProtocolName;
}
_lastProtocolName ??= ProtocolName;
return _lastProtocolName == NegotiationInfoClass.Kerberos;
}
}

return (object)_lastProtocolName == (object)NegotiationInfoClass.Kerberos;
internal bool IsNTLM
{
get
{
_lastProtocolName ??= ProtocolName;
return _lastProtocolName == NegotiationInfoClass.NTLM;
}
}

Expand Down Expand Up @@ -150,6 +155,7 @@ internal void CloseContext()
{
_securityContext.Dispose();
}
_isCompleted = false;
}

internal int VerifySignature(byte[] buffer, int offset, int count)
Expand Down Expand Up @@ -204,11 +210,16 @@ internal int MakeSignature(byte[] buffer, int offset, int count, [AllowNull] ref

internal byte[]? GetOutgoingBlob(byte[]? incomingBlob, bool throwOnError)
{
return GetOutgoingBlob(incomingBlob, throwOnError, out _);
return GetOutgoingBlob(incomingBlob.AsSpan(), throwOnError, out _);
}

// Accepts an incoming binary security blob and returns an outgoing binary security blob.
internal byte[]? GetOutgoingBlob(byte[]? incomingBlob, bool throwOnError, out SecurityStatusPal statusCode)
{
return GetOutgoingBlob(incomingBlob.AsSpan(), throwOnError, out statusCode);
}

internal byte[]? GetOutgoingBlob(ReadOnlySpan<byte> incomingBlob, bool throwOnError, out SecurityStatusPal statusCode)
{
byte[]? result = new byte[_tokenSize];

Expand Down Expand Up @@ -312,5 +323,29 @@ internal int MakeSignature(byte[] buffer, int offset, int count, [AllowNull] ref

return spn;
}

internal int Encrypt(ReadOnlySpan<byte> buffer, [NotNull] ref byte[]? output, uint sequenceNumber)
{
return NegotiateStreamPal.Encrypt(
_securityContext!,
buffer,
(_contextFlags & ContextFlagsPal.Confidentiality) != 0,
IsNTLM,
ref output,
sequenceNumber);
}

internal int Decrypt(byte[] payload, int offset, int count, out int newOffset, uint expectedSeqNumber)
{
return NegotiateStreamPal.Decrypt(
_securityContext!,
payload,
offset,
count,
(_contextFlags & ContextFlagsPal.Confidentiality) != 0,
IsNTLM,
out newOffset,
expectedSeqNumber);
}
}
}
Loading

0 comments on commit 79f6709

Please sign in to comment.