-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Implement NegotiateAuthentication API #70720
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
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 c7a6bce
WIP: Update error code mapping
filipnavara 5d5c61f
Spanify input of GetOutgoingBlob
filipnavara d0d1bba
Update comments
filipnavara 982e7c3
Move NegotiateStreamPal.Encrypt/Decrypt to shared sources. Unix imple…
filipnavara cbd24a2
Revert accidental change
filipnavara 9d4d557
Build fixes.
filipnavara c371515
Fix error handling condition
filipnavara 3306c67
Update error mapping based on HttpListener usage.
filipnavara 065d87c
WIP: HttpListener test
filipnavara 447020a
Move workaround from HttpListener to low-level SSPI code
filipnavara bed08fa
Fix build
filipnavara 318ca06
Clean up
filipnavara 5474811
Revert "WIP: HttpListener test"
filipnavara 4cdb8af
Convert System.Net.Http.FunctionalTests to use NegotiateAuthenticatio…
filipnavara 34e47c4
Dispose the identity along NegotiateAuthentication
filipnavara 73ac446
Modify unit tests to use the new API
filipnavara 7e85f10
Add exceptions for invalid inputs/states
filipnavara 7c870bd
Remove tvOS unsupported marker, managed NTLM is used on tvOS
filipnavara 1bd0cde
Apply suggestions from code review
filipnavara e93f073
Fix typo
filipnavara 5aae44d
Remove reference equality checks from IsNTLM/IsKerberos
filipnavara b746590
Remove NTAuthentication.AssociatedName to make it more obvious which …
filipnavara dd7dd9a
Add comment
filipnavara 39a43b2
Add more tests, handle unsupported protocols
filipnavara 9a4ccdf
Handle NotSupportedException from NTAuthentication constructor
filipnavara e86ce79
Add workaround for linker issue
filipnavara ffed0be
Apply suggestions from code review
filipnavara File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Move NegotiateStreamPal.Encrypt/Decrypt to shared sources. Unix imple…
…mentation already had them and they get trimmed anyway.
- Loading branch information
commit 982e7c32297619acb0356515b94e5c7f4a988daf
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
||
| // 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; | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.