This repository was archived by the owner on Jan 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Enable NegotiateStream For Unix #6469
Merged
Merged
Changes from all commits
Commits
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
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
128 changes: 128 additions & 0 deletions
128
src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssApi.cs
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 |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| using System; | ||
| using System.Diagnostics; | ||
| using System.Runtime.InteropServices; | ||
| using System.Text; | ||
| using Microsoft.Win32.SafeHandles; | ||
|
|
||
| internal static partial class Interop | ||
| { | ||
| internal static class GssApi | ||
| { | ||
| internal static bool EstablishSecurityContext( | ||
| ref SafeGssContextHandle context, | ||
| SafeGssCredHandle credential, | ||
| bool isNtlm, | ||
| SafeGssNameHandle targetName, | ||
| Interop.NetSecurityNative.GssFlags inFlags, | ||
| byte[] buffer, | ||
| out byte[] outputBuffer, | ||
| out uint outFlags) | ||
| { | ||
| outputBuffer = null; | ||
| outFlags = 0; | ||
|
|
||
| // EstablishSecurityContext is called multiple times in a session. | ||
| // In each call, we need to pass the context handle from the previous call. | ||
| // For the first call, the context handle will be null. | ||
| if (context == null) | ||
| { | ||
| context = new SafeGssContextHandle(); | ||
| } | ||
|
|
||
| Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer); | ||
| Interop.NetSecurityNative.Status status; | ||
|
|
||
| try | ||
| { | ||
| Interop.NetSecurityNative.Status minorStatus; | ||
| status = NetSecurityNative.InitSecContext(out minorStatus, | ||
| credential, | ||
| ref context, | ||
| isNtlm, | ||
| targetName, | ||
| (uint)inFlags, | ||
| buffer, | ||
| (buffer == null) ? 0 : buffer.Length, | ||
| ref token, | ||
| out outFlags); | ||
|
|
||
| if ((status != NetSecurityNative.Status.GSS_S_COMPLETE) && (status != NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED)) | ||
| { | ||
| throw new NetSecurityNative.GssApiException(status, minorStatus); | ||
| } | ||
|
|
||
| outputBuffer = token.ToByteArray(); | ||
| } | ||
| finally | ||
| { | ||
| token.Dispose(); | ||
| } | ||
|
|
||
| return status == NetSecurityNative.Status.GSS_S_COMPLETE; | ||
| } | ||
|
|
||
| internal static byte[] Encrypt( | ||
| SafeGssContextHandle context, | ||
| bool encrypt, | ||
| byte[] buffer, | ||
| int offset, | ||
| int count) | ||
| { | ||
| Debug.Assert((buffer != null) && (buffer.Length > 0), "Invalid input buffer passed to Encrypt"); | ||
| Debug.Assert((offset >= 0) && (offset < buffer.Length), "Invalid input offset passed to Encrypt"); | ||
| Debug.Assert((count >= 0) && (count <= (buffer.Length - offset)), "Invalid input count passed to Encrypt"); | ||
|
|
||
| Interop.NetSecurityNative.GssBuffer encryptedBuffer = default(Interop.NetSecurityNative.GssBuffer); | ||
| try | ||
| { | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: unnecessary blank line |
||
| NetSecurityNative.Status minorStatus; | ||
| NetSecurityNative.Status status = NetSecurityNative.WrapBuffer(out minorStatus, context, encrypt, buffer, offset, count, ref encryptedBuffer); | ||
| if (status != NetSecurityNative.Status.GSS_S_COMPLETE) | ||
| { | ||
| throw new NetSecurityNative.GssApiException(status, minorStatus); | ||
| } | ||
|
|
||
| return encryptedBuffer.ToByteArray(); | ||
| } | ||
| finally | ||
| { | ||
| encryptedBuffer.Dispose(); | ||
| } | ||
| } | ||
|
|
||
| internal static int Decrypt( | ||
| SafeGssContextHandle context, | ||
| byte[] buffer, | ||
| int offset, | ||
| int count) | ||
| { | ||
| Debug.Assert((buffer != null) && (buffer.Length > 0), "Invalid input buffer passed to Decrypt"); | ||
| Debug.Assert((offset >= 0) && (offset <= buffer.Length), "Invalid input offset passed to Decrypt"); | ||
| Debug.Assert((count >= 0) && (count <= (buffer.Length - offset)), "Invalid input count passed to Decrypt"); | ||
|
|
||
| Interop.NetSecurityNative.GssBuffer decryptedBuffer = default(Interop.NetSecurityNative.GssBuffer); | ||
| try | ||
| { | ||
| NetSecurityNative.Status minorStatus; | ||
| NetSecurityNative.Status status = NetSecurityNative.UnwrapBuffer(out minorStatus, context, buffer, offset, count, ref decryptedBuffer); | ||
| if (status != NetSecurityNative.Status.GSS_S_COMPLETE) | ||
| { | ||
| throw new NetSecurityNative.GssApiException(status, minorStatus); | ||
| } | ||
|
|
||
| return decryptedBuffer.Copy(buffer, offset); | ||
| } | ||
| finally | ||
| { | ||
| decryptedBuffer.Dispose(); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
63 changes: 63 additions & 0 deletions
63
src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssApiException.cs
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 |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.Runtime.InteropServices; | ||
|
|
||
| internal static partial class Interop | ||
| { | ||
| internal static partial class NetSecurityNative | ||
| { | ||
| internal sealed class GssApiException : Exception | ||
| { | ||
| private readonly Status _minorStatus; | ||
|
|
||
| public Status MinorStatus | ||
| { | ||
| get { return _minorStatus;} | ||
| } | ||
|
|
||
| public GssApiException(string message) : base(message) | ||
| { | ||
| } | ||
|
|
||
| public GssApiException(Status majorStatus, Status minorStatus) | ||
| : base(GetGssApiDisplayStatus(majorStatus, minorStatus)) | ||
| { | ||
| HResult = (int)majorStatus; | ||
| _minorStatus = minorStatus; | ||
| } | ||
|
|
||
| private static string GetGssApiDisplayStatus(Status majorStatus, Status minorStatus) | ||
| { | ||
| string majorError = GetGssApiDisplayStatus(majorStatus, isMinor: false); | ||
| string minorError = GetGssApiDisplayStatus(minorStatus, isMinor: true); | ||
|
|
||
| return (majorError != null && minorError != null) ? | ||
| SR.Format(SR.net_gssapi_operation_failed_detailed, majorError, minorError) : | ||
| SR.Format(SR.net_gssapi_operation_failed, majorStatus.ToString("x"), minorStatus.ToString("x")); | ||
| } | ||
|
|
||
| private static string GetGssApiDisplayStatus(Status status, bool isMinor) | ||
| { | ||
| GssBuffer displayBuffer = default(GssBuffer); | ||
|
|
||
| try | ||
| { | ||
| Interop.NetSecurityNative.Status minStat; | ||
| Interop.NetSecurityNative.Status displayCallStatus = isMinor ? | ||
| DisplayMinorStatus(out minStat, status, ref displayBuffer): | ||
| DisplayMajorStatus(out minStat, status, ref displayBuffer); | ||
| return (Status.GSS_S_COMPLETE != displayCallStatus) ? null : Marshal.PtrToStringAnsi(displayBuffer._data); | ||
| } | ||
| finally | ||
| { | ||
| displayBuffer.Dispose(); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
67 changes: 67 additions & 0 deletions
67
src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs
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 |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| using System; | ||
| using System.Diagnostics; | ||
| using System.Runtime.InteropServices; | ||
| using Microsoft.Win32.SafeHandles; | ||
|
|
||
| internal static partial class Interop | ||
| { | ||
| internal static partial class NetSecurityNative | ||
| { | ||
| [StructLayout(LayoutKind.Sequential)] | ||
| internal unsafe struct GssBuffer : IDisposable | ||
| { | ||
| internal UInt64 _length; | ||
| internal IntPtr _data; | ||
|
|
||
| internal int Copy(byte[] destination, int offset) | ||
| { | ||
| Debug.Assert(destination != null, "target destination cannot be null"); | ||
| Debug.Assert((offset >= 0 && offset < destination.Length) || destination.Length == 0, "invalid offset " + offset); | ||
|
|
||
| if (_data == IntPtr.Zero || _length == 0) | ||
| { | ||
| return 0; | ||
| } | ||
|
|
||
| // Using Convert.ToInt32 to throw an exception in the unlikely event of too large value of _length | ||
| int sourceLength = Convert.ToInt32(_length); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the intention here is to have it throw for a too-large value, a comment to that effect would be useful. |
||
| int destinationAvailable = destination.Length - offset; // amount of space in the given buffer | ||
| if (sourceLength > destinationAvailable) | ||
| { | ||
| throw new NetSecurityNative.GssApiException(SR.Format(SR.net_context_buffer_too_small, sourceLength, destinationAvailable)); | ||
| } | ||
|
|
||
| Marshal.Copy(_data, destination, offset, sourceLength); | ||
| return sourceLength; | ||
| } | ||
|
|
||
| internal byte[] ToByteArray() | ||
| { | ||
| if (_data == IntPtr.Zero || _length == 0) | ||
| { | ||
| return Array.Empty<byte>(); | ||
| } | ||
|
|
||
| int destinationLength = Convert.ToInt32(_length); | ||
| byte[] destination = new byte[destinationLength]; | ||
| Marshal.Copy(_data, destination, 0, destinationLength); | ||
| return destination; | ||
| } | ||
|
|
||
| public void Dispose() | ||
| { | ||
| if (_data != IntPtr.Zero) | ||
| { | ||
| Interop.NetSecurityNative.ReleaseGssBuffer(_data, _length); | ||
| _data = IntPtr.Zero; | ||
| } | ||
|
|
||
| _length = 0; | ||
| } | ||
| } | ||
| } | ||
| } | ||
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't remember if we've had this conversation before, but...
This is a lot of code being added to the Interop.*.cs files. Typically we've kept these as close to 1:1 with the underlying operation being wrapped as possible, typiclaly just the P/Invoke signature or maybe a 1-2 line wrapper if necessary, and then all of this additional logic ends up living in the consuming code.
Is there a reason it's done this way instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Note that at this point I'm not suggesting we rework all of this before it's merged, but rather it should be done as cleanup after merging.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is being tracked via #7031 (item no 1)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only reason we started down this path with Interop.OpenSsl.cs was to allow sharing of the interop code across libraries (eg. System.Net.HttpClient and System.Net.Security) since the 1:1 functions are not very usable by themselves. So if we are sure there is no other consumer for GSSAPI library so we can move all the logic back to NegotiateStreamPal.Unix.cs (or still keep as separate source file but under System.Net.Security\src)