-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Implement server-side of NegotiateStream on Unix #36827
Changes from all commits
942537e
055e309
87031a1
2dc068d
46e60d1
96f9500
bb98da1
d6166fd
7e9b140
fd5657e
e09ee83
9285c5b
d542839
84adb5c
8d774be
48a888a
0b0ea1f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,14 +3,15 @@ | |
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| using System.IO; | ||
| using System.Security; | ||
| using System.Security.Principal; | ||
| using System.Threading; | ||
| using System.ComponentModel; | ||
| using System.Diagnostics; | ||
| using System.Runtime.InteropServices; | ||
| using System.Security; | ||
| using System.Security.Authentication; | ||
| using System.Security.Authentication.ExtendedProtection; | ||
| using System.Security.Principal; | ||
| using System.Text; | ||
| using System.Threading; | ||
| using Microsoft.Win32.SafeHandles; | ||
|
|
||
| namespace System.Net.Security | ||
|
|
@@ -124,8 +125,10 @@ private static bool GssInitSecurityContext( | |
| // 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. | ||
| bool newContext = false; | ||
| if (context == null) | ||
| { | ||
| newContext = true; | ||
| context = new SafeGssContextHandle(); | ||
| } | ||
|
|
||
|
|
@@ -152,6 +155,11 @@ private static bool GssInitSecurityContext( | |
| if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) && | ||
| (status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED)) | ||
| { | ||
| if (newContext) | ||
| { | ||
| context.Dispose(); | ||
| context = null; | ||
| } | ||
| throw new Interop.NetSecurityNative.GssApiException(status, minorStatus); | ||
| } | ||
|
|
||
|
|
@@ -165,6 +173,78 @@ private static bool GssInitSecurityContext( | |
| return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE; | ||
| } | ||
|
|
||
| private static bool GssAcceptSecurityContext( | ||
| ref SafeGssContextHandle context, | ||
| byte[] buffer, | ||
| out byte[] outputBuffer, | ||
| out uint outFlags) | ||
| { | ||
| bool newContext = false; | ||
| if (context == null) | ||
| { | ||
| newContext = true; | ||
| context = new SafeGssContextHandle(); | ||
| } | ||
|
|
||
| Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer); | ||
| Interop.NetSecurityNative.Status status; | ||
|
|
||
| try | ||
| { | ||
| Interop.NetSecurityNative.Status minorStatus; | ||
| status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus, | ||
| ref context, | ||
| buffer, | ||
| buffer?.Length ?? 0, | ||
| ref token, | ||
| out outFlags); | ||
|
|
||
| if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) && | ||
| (status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED)) | ||
| { | ||
| if (newContext) | ||
| { | ||
| context.Dispose(); | ||
| context = null; | ||
| } | ||
| throw new Interop.NetSecurityNative.GssApiException(status, minorStatus); | ||
| } | ||
|
|
||
| outputBuffer = token.ToByteArray(); | ||
| } | ||
| finally | ||
| { | ||
| token.Dispose(); | ||
| } | ||
|
|
||
| return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE; | ||
| } | ||
|
|
||
| private static string GssGetUser( | ||
| ref SafeGssContextHandle context) | ||
| { | ||
| Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer); | ||
|
|
||
| try | ||
| { | ||
| Interop.NetSecurityNative.Status status | ||
| = Interop.NetSecurityNative.GetUser(out var minorStatus, | ||
| context, | ||
| ref token); | ||
|
|
||
| if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) | ||
| { | ||
| throw new Interop.NetSecurityNative.GssApiException(status, minorStatus); | ||
| } | ||
|
|
||
| return Encoding.UTF8.GetString(token.ToByteArray()); | ||
Tratcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| finally | ||
| { | ||
| token.Dispose(); | ||
| } | ||
| } | ||
|
|
||
| private static SecurityStatusPal EstablishSecurityContext( | ||
| SafeFreeNegoCredentials credential, | ||
| ref SafeDeleteContext context, | ||
|
|
@@ -293,7 +373,61 @@ internal static SecurityStatusPal AcceptSecurityContext( | |
| ref byte[] resultBlob, | ||
| ref ContextFlagsPal contextFlags) | ||
| { | ||
| throw new PlatformNotSupportedException(SR.net_nego_server_not_supported); | ||
|
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. Can we delete the associated string from the .resx, or is it still needed?
Contributor
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. I think it can be deleted.
Member
Author
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. It's still referenced in a few places.
Contributor
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. I looked at the places it's used. We can leave the string in the .resx for now. But I think we should be able to eventually fix the other PlatformNotSupportedException's later and remove the string. |
||
| if (securityContext == null) | ||
| { | ||
| securityContext = new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle); | ||
| } | ||
|
|
||
| SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext; | ||
| try | ||
| { | ||
| SafeGssContextHandle contextHandle = negoContext.GssContext; | ||
| bool done = GssAcceptSecurityContext( | ||
| ref contextHandle, | ||
| incomingBlob, | ||
| out resultBlob, | ||
| out uint outputFlags); | ||
|
|
||
| Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi"); | ||
| Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext); | ||
|
|
||
| // Save the inner context handle for further calls to NetSecurity | ||
| Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext); | ||
| if (null == negoContext.GssContext) | ||
| { | ||
| negoContext.SetGssContext(contextHandle); | ||
| } | ||
|
|
||
| contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop( | ||
| (Interop.NetSecurityNative.GssFlags)outputFlags, isServer: true); | ||
|
|
||
| SecurityStatusPalErrorCode errorCode = done ? | ||
| (negoContext.IsNtlmUsed && resultBlob.Length > 0 ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded) : | ||
| SecurityStatusPalErrorCode.ContinueNeeded; | ||
|
|
||
| return new SecurityStatusPal(errorCode); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| if (NetEventSource.IsEnabled) NetEventSource.Error(null, ex); | ||
| return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex); | ||
| } | ||
| } | ||
|
|
||
| private static string GetUser( | ||
| ref SafeDeleteContext securityContext) | ||
| { | ||
| SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext; | ||
| try | ||
| { | ||
| SafeGssContextHandle contextHandle = negoContext.GssContext; | ||
| return GssGetUser(ref contextHandle); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| if (NetEventSource.IsEnabled) NetEventSource.Error(null, ex); | ||
| throw; | ||
| } | ||
| } | ||
|
|
||
| internal static Win32Exception CreateExceptionFromError(SecurityStatusPal statusCode) | ||
|
|
@@ -314,11 +448,6 @@ internal static SafeFreeCredentials AcquireDefaultCredential(string package, boo | |
|
|
||
| internal static SafeFreeCredentials AcquireCredentialsHandle(string package, bool isServer, NetworkCredential credential) | ||
| { | ||
| if (isServer) | ||
| { | ||
| throw new PlatformNotSupportedException(SR.net_nego_server_not_supported); | ||
| } | ||
|
|
||
| bool isEmptyCredential = string.IsNullOrWhiteSpace(credential.UserName) || | ||
| string.IsNullOrWhiteSpace(credential.Password); | ||
| bool ntlmOnly = string.Equals(package, NegotiationInfoClass.NTLM, StringComparison.OrdinalIgnoreCase); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.