diff options
author | Egor Bogatov <egorbo@gmail.com> | 2019-10-23 23:08:50 +0300 |
---|---|---|
committer | Steve Pfister <steveisok@users.noreply.github.com> | 2019-10-23 23:08:50 +0300 |
commit | fb41040a9ee62f266e7c6684897b6b0368fdfff1 (patch) | |
tree | b13809ff855f5de7a57a3932c40d0304548fa7c1 /src | |
parent | 8e3b279377d1f01d2d089f24548e33855bf6502e (diff) |
Update GSS/NTLM-related sources (from .NET Core 3.0 and 2.2) (#362)
Diffstat (limited to 'src')
10 files changed, 409 insertions, 147 deletions
diff --git a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssApiException.cs b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssApiException.cs index 985a971dd4..b6ea34d647 100644 --- a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssApiException.cs +++ b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssApiException.cs @@ -15,9 +15,14 @@ internal static partial class Interop { private readonly Status _minorStatus; + public Status MajorStatus + { + get { return (Status)HResult; } + } + public Status MinorStatus { - get { return _minorStatus;} + get { return _minorStatus; } } public GssApiException(string message) : base(message) @@ -25,20 +30,44 @@ internal static partial class Interop } public GssApiException(Status majorStatus, Status minorStatus) - : base(GetGssApiDisplayStatus(majorStatus, minorStatus)) + : base(GetGssApiDisplayStatus(majorStatus, minorStatus, null)) + { + HResult = (int)majorStatus; + _minorStatus = minorStatus; + } + + public GssApiException(Status majorStatus, Status minorStatus, string helpText) + : base(GetGssApiDisplayStatus(majorStatus, minorStatus, helpText)) { HResult = (int)majorStatus; _minorStatus = minorStatus; } - private static string GetGssApiDisplayStatus(Status majorStatus, Status minorStatus) + private static string GetGssApiDisplayStatus(Status majorStatus, Status minorStatus, string helpText) { string majorError = GetGssApiDisplayStatus(majorStatus, isMinor: false); - string minorError = GetGssApiDisplayStatus(minorStatus, isMinor: true); + string errorMessage; + + if (minorStatus != Status.GSS_S_COMPLETE) + { + string minorError = GetGssApiDisplayStatus(minorStatus, isMinor: true); + errorMessage = (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")); + } + else + { + errorMessage = (majorError != null) ? + SR.Format(SR.net_gssapi_operation_failed_detailed_majoronly, majorError) : + SR.Format(SR.net_gssapi_operation_failed_majoronly, majorStatus.ToString("x")); + } + + if (!string.IsNullOrEmpty(helpText)) + { + return errorMessage + " " + helpText; + } - 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")); + return errorMessage; } private static string GetGssApiDisplayStatus(Status status, bool isMinor) @@ -60,4 +89,4 @@ internal static partial class Interop } } } -} +}
\ No newline at end of file diff --git a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs index 3168200d7e..231bd2071a 100644 --- a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs +++ b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs @@ -14,7 +14,7 @@ internal static partial class Interop [StructLayout(LayoutKind.Sequential)] internal unsafe struct GssBuffer : IDisposable { - internal UInt64 _length; + internal ulong _length; internal IntPtr _data; internal int Copy(byte[] destination, int offset) @@ -66,11 +66,11 @@ internal static partial class Interop #if DEBUG static GssBuffer() { - // Verify managed size on both 32-bit and 64-bit matches the PAL_GssBuffer + // Verify managed size on both 32-bit and 64-bit matches the PAL_GssBuffer // native struct size, which is also padded on 32-bit. Debug.Assert(Marshal.SizeOf<GssBuffer>() == 16); } #endif } } -} +}
\ No newline at end of file diff --git a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Initialization.cs b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Initialization.cs index 44ae8a66ee..3794522485 100644 --- a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Initialization.cs +++ b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Initialization.cs @@ -38,4 +38,4 @@ internal static partial class Interop // No-op that exists to provide a hook for other static constructors } } -} +}
\ No newline at end of file diff --git a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs index 95d159256a..753c6be69b 100644 --- a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs +++ b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs @@ -12,65 +12,55 @@ internal static partial class Interop { internal static partial class NetSecurityNative { -#if ENABLE_GSS - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ReleaseGssBuffer")] - internal static extern void ReleaseGssBuffer( +#if !ENABLE_GSS + internal static void ReleaseGssBuffer( IntPtr bufferPtr, - UInt64 length); + ulong length) => throw new PlatformNotSupportedException (); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_DisplayMinorStatus")] - internal static extern Status DisplayMinorStatus( + internal static Status DisplayMinorStatus( out Status minorStatus, Status statusValue, - ref GssBuffer buffer); + ref GssBuffer buffer) => throw new PlatformNotSupportedException (); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_DisplayMajorStatus")] - internal static extern Status DisplayMajorStatus( + internal static Status DisplayMajorStatus( out Status minorStatus, Status statusValue, - ref GssBuffer buffer); + ref GssBuffer buffer) => throw new PlatformNotSupportedException (); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ImportUserName")] - internal static extern Status ImportUserName( + internal static Status ImportUserName( out Status minorStatus, string inputName, int inputNameByteCount, - out SafeGssNameHandle outputName); + out SafeGssNameHandle outputName) => throw new PlatformNotSupportedException (); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ImportPrincipalName")] - internal static extern Status ImportPrincipalName( + internal static Status ImportPrincipalName( out Status minorStatus, string inputName, int inputNameByteCount, - out SafeGssNameHandle outputName); + out SafeGssNameHandle outputName) => throw new PlatformNotSupportedException (); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ReleaseName")] - internal static extern Status ReleaseName( + internal static Status ReleaseName( out Status minorStatus, - ref IntPtr inputName); + ref IntPtr inputName) => throw new PlatformNotSupportedException (); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_InitiateCredSpNego")] - internal static extern Status InitiateCredSpNego( + internal static Status InitiateCredSpNego( out Status minorStatus, SafeGssNameHandle desiredName, - out SafeGssCredHandle outputCredHandle); + out SafeGssCredHandle outputCredHandle) => throw new PlatformNotSupportedException (); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_InitiateCredWithPassword")] - internal static extern Status InitiateCredWithPassword( + internal static Status InitiateCredWithPassword( out Status minorStatus, bool isNtlm, SafeGssNameHandle desiredName, string password, int passwordLen, - out SafeGssCredHandle outputCredHandle); + out SafeGssCredHandle outputCredHandle) => throw new PlatformNotSupportedException (); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ReleaseCred")] - internal static extern Status ReleaseCred( + internal static Status ReleaseCred( out Status minorStatus, - ref IntPtr credHandle); + ref IntPtr credHandle) => throw new PlatformNotSupportedException (); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_InitSecContext")] - internal static extern Status InitSecContext( + internal static Status InitSecContext( out Status minorStatus, SafeGssCredHandle initiatorCredHandle, ref SafeGssContextHandle contextHandle, @@ -81,119 +71,115 @@ internal static partial class Interop int inputLength, ref GssBuffer token, out uint retFlags, - out int isNtlmUsed); + out int isNtlmUsed) => throw new PlatformNotSupportedException (); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_AcceptSecContext")] - internal static extern Status AcceptSecContext( + internal static Status InitSecContext( out Status minorStatus, - ref SafeGssContextHandle acceptContextHandle, + SafeGssCredHandle initiatorCredHandle, + ref SafeGssContextHandle contextHandle, + bool isNtlmOnly, + IntPtr cbt, + int cbtSize, + SafeGssNameHandle targetName, + uint reqFlags, byte[] inputBytes, int inputLength, - ref GssBuffer token); + ref GssBuffer token, + out uint retFlags, + out int isNtlmUsed) => throw new PlatformNotSupportedException (); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_DeleteSecContext")] - internal static extern Status DeleteSecContext( + internal static Status AcceptSecContext( out Status minorStatus, - ref IntPtr contextHandle); + ref SafeGssContextHandle acceptContextHandle, + byte[] inputBytes, + int inputLength, + ref GssBuffer token, + out uint retFlags) => throw new PlatformNotSupportedException (); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_Wrap")] - private static extern Status Wrap( + internal static Status DeleteSecContext( out Status minorStatus, - SafeGssContextHandle contextHandle, - bool isEncrypt, - byte[] inputBytes, - int offset, - int count, - ref GssBuffer outBuffer); + ref IntPtr contextHandle) => throw new PlatformNotSupportedException (); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_Unwrap")] - private static extern Status Unwrap( + internal static Status GetUser( out Status minorStatus, - SafeGssContextHandle contextHandle, - byte[] inputBytes, - int offset, - int count, - ref GssBuffer outBuffer); + SafeGssContextHandle acceptContextHandle, + ref GssBuffer token) => throw new PlatformNotSupportedException (); - internal static Status WrapBuffer( + private static Status Wrap( out Status minorStatus, SafeGssContextHandle contextHandle, bool isEncrypt, byte[] inputBytes, int offset, int count, - ref GssBuffer outBuffer) - { - Debug.Assert(inputBytes != null, "inputBytes must be valid value"); - Debug.Assert(offset >= 0 && offset <= inputBytes.Length, "offset must be valid"); - Debug.Assert(count >= 0 && count <= (inputBytes.Length - offset), "count must be valid"); + ref GssBuffer outBuffer) => throw new PlatformNotSupportedException (); - return Wrap(out minorStatus, contextHandle, isEncrypt, inputBytes, offset, count, ref outBuffer); - } - - internal static Status UnwrapBuffer( + private static Status Unwrap( out Status minorStatus, SafeGssContextHandle contextHandle, byte[] inputBytes, int offset, int count, - ref GssBuffer outBuffer) - { - Debug.Assert(inputBytes != null, "inputBytes must be valid value"); - Debug.Assert(offset >= 0 && offset <= inputBytes.Length, "offset must be valid"); - Debug.Assert(count >= 0 && count <= inputBytes.Length, "count must be valid"); - - return Unwrap(out minorStatus, contextHandle, inputBytes, offset, count, ref outBuffer); - } + ref GssBuffer outBuffer) => throw new PlatformNotSupportedException (); #else - internal static void ReleaseGssBuffer ( + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ReleaseGssBuffer")] + internal static extern void ReleaseGssBuffer( IntPtr bufferPtr, - UInt64 length) => throw new NotSupportedException (); + ulong length); - internal static Status DisplayMinorStatus ( + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_DisplayMinorStatus")] + internal static extern Status DisplayMinorStatus( out Status minorStatus, Status statusValue, - ref GssBuffer buffer) => throw new NotSupportedException (); + ref GssBuffer buffer); - internal static Status DisplayMajorStatus ( + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_DisplayMajorStatus")] + internal static extern Status DisplayMajorStatus( out Status minorStatus, Status statusValue, - ref GssBuffer buffer) => throw new NotSupportedException (); + ref GssBuffer buffer); - internal static Status ImportUserName ( + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ImportUserName")] + internal static extern Status ImportUserName( out Status minorStatus, string inputName, int inputNameByteCount, - out SafeGssNameHandle outputName) => throw new NotSupportedException (); + out SafeGssNameHandle outputName); - internal static Status ImportPrincipalName ( + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ImportPrincipalName")] + internal static extern Status ImportPrincipalName( out Status minorStatus, string inputName, int inputNameByteCount, - out SafeGssNameHandle outputName) => throw new NotSupportedException (); + out SafeGssNameHandle outputName); - internal static Status ReleaseName ( + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ReleaseName")] + internal static extern Status ReleaseName( out Status minorStatus, - ref IntPtr inputName) => throw new NotSupportedException (); + ref IntPtr inputName); - internal static Status InitiateCredSpNego ( + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_InitiateCredSpNego")] + internal static extern Status InitiateCredSpNego( out Status minorStatus, SafeGssNameHandle desiredName, - out SafeGssCredHandle outputCredHandle) => throw new NotSupportedException (); + out SafeGssCredHandle outputCredHandle); - internal static Status InitiateCredWithPassword ( + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_InitiateCredWithPassword")] + internal static extern Status InitiateCredWithPassword( out Status minorStatus, bool isNtlm, SafeGssNameHandle desiredName, string password, int passwordLen, - out SafeGssCredHandle outputCredHandle) => throw new NotSupportedException (); + out SafeGssCredHandle outputCredHandle); - internal static Status ReleaseCred ( + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ReleaseCred")] + internal static extern Status ReleaseCred( out Status minorStatus, - ref IntPtr credHandle) => throw new NotSupportedException (); + ref IntPtr credHandle); - internal static Status InitSecContext ( + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_InitSecContext")] + internal static extern Status InitSecContext( out Status minorStatus, SafeGssCredHandle initiatorCredHandle, ref SafeGssContextHandle contextHandle, @@ -204,58 +190,121 @@ internal static partial class Interop int inputLength, ref GssBuffer token, out uint retFlags, - out int isNtlmUsed) => throw new NotSupportedException (); + out int isNtlmUsed); - internal static Status AcceptSecContext ( + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_InitSecContextEx")] + internal static extern Status InitSecContext( + out Status minorStatus, + SafeGssCredHandle initiatorCredHandle, + ref SafeGssContextHandle contextHandle, + bool isNtlmOnly, + IntPtr cbt, + int cbtSize, + SafeGssNameHandle targetName, + uint reqFlags, + byte[] inputBytes, + int inputLength, + ref GssBuffer token, + out uint retFlags, + out int isNtlmUsed); + + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_AcceptSecContext")] + internal static extern Status AcceptSecContext( out Status minorStatus, ref SafeGssContextHandle acceptContextHandle, byte[] inputBytes, int inputLength, - ref GssBuffer token) => throw new NotSupportedException (); + ref GssBuffer token, + out uint retFlags); + + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_DeleteSecContext")] + internal static extern Status DeleteSecContext( + out Status minorStatus, + ref IntPtr contextHandle); - internal static Status DeleteSecContext ( + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_GetUser")] + internal static extern Status GetUser( out Status minorStatus, - ref IntPtr contextHandle) => throw new NotSupportedException (); + SafeGssContextHandle acceptContextHandle, + ref GssBuffer token); - static Status Wrap( + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_Wrap")] + private static extern Status Wrap( out Status minorStatus, SafeGssContextHandle contextHandle, bool isEncrypt, byte[] inputBytes, int offset, int count, - ref GssBuffer outBuffer) => throw new NotSupportedException (); + ref GssBuffer outBuffer); - static Status Unwrap ( + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_Unwrap")] + private static extern Status Unwrap( out Status minorStatus, SafeGssContextHandle contextHandle, byte[] inputBytes, int offset, int count, - ref GssBuffer outBuffer) => throw new NotSupportedException (); + ref GssBuffer outBuffer); +#endif - internal static Status WrapBuffer ( + internal static Status WrapBuffer( out Status minorStatus, SafeGssContextHandle contextHandle, bool isEncrypt, byte[] inputBytes, int offset, int count, - ref GssBuffer outBuffer) => throw new NotSupportedException (); + ref GssBuffer outBuffer) + { + Debug.Assert(inputBytes != null, "inputBytes must be valid value"); + Debug.Assert(offset >= 0 && offset <= inputBytes.Length, "offset must be valid"); + Debug.Assert(count >= 0 && count <= (inputBytes.Length - offset), "count must be valid"); - internal static Status UnwrapBuffer ( + return Wrap(out minorStatus, contextHandle, isEncrypt, inputBytes, offset, count, ref outBuffer); + } + + internal static Status UnwrapBuffer( out Status minorStatus, SafeGssContextHandle contextHandle, byte[] inputBytes, int offset, int count, - ref GssBuffer outBuffer) => throw new NotSupportedException (); -#endif + ref GssBuffer outBuffer) + { + Debug.Assert(inputBytes != null, "inputBytes must be valid value"); + Debug.Assert(offset >= 0 && offset <= inputBytes.Length, "offset must be valid"); + Debug.Assert(count >= 0 && count <= inputBytes.Length, "count must be valid"); + + return Unwrap(out minorStatus, contextHandle, inputBytes, offset, count, ref outBuffer); + } + + // https://www.gnu.org/software/gss/reference/gss.pdf Page 65 + internal const int GSS_C_ROUTINE_ERROR_OFFSET = 16; + // https://www.gnu.org/software/gss/reference/gss.pdf Page 9 internal enum Status : uint { GSS_S_COMPLETE = 0, - GSS_S_CONTINUE_NEEDED = 1 + GSS_S_CONTINUE_NEEDED = 1, + GSS_S_BAD_MECH = 1 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_BAD_NAME = 2 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_BAD_NAMETYPE = 3 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_BAD_BINDINGS = 4 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_BAD_STATUS = 5 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_BAD_SIG = 6 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_NO_CRED = 7 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_NO_CONTEXT = 8 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_DEFECTIVE_TOKEN = 9 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_DEFECTIVE_CREDENTIAL = 10 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_CREDENTIALS_EXPIRED = 11 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_CONTEXT_EXPIRED = 12 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_FAILURE = 13 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_BAD_QOP = 14 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_UNAUTHORIZED = 15 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_UNAVAILABLE = 16 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_DUPLICATE_ELEMENT = 17 << GSS_C_ROUTINE_ERROR_OFFSET, + GSS_S_NAME_NOT_MN = 18 << GSS_C_ROUTINE_ERROR_OFFSET, } [Flags] @@ -276,4 +325,4 @@ internal static partial class Interop GSS_C_DELEG_POLICY_FLAG = 0x8000 } } -} +}
\ No newline at end of file diff --git a/src/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.cs b/src/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.cs index 6daf3f2191..28dabc477e 100644 --- a/src/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.cs +++ b/src/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.cs @@ -146,4 +146,4 @@ namespace Microsoft.Win32.SafeHandles return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE; } } -} +}
\ No newline at end of file diff --git a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs index 74ed1b5d61..ee1f35dc98 100644 --- a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs +++ b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs @@ -413,4 +413,4 @@ namespace System.Net.Security return resultSize + 4; } } -} +}
\ No newline at end of file diff --git a/src/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs b/src/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs index 9b07e53e5e..ec1287c284 100644 --- a/src/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs +++ b/src/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs @@ -77,4 +77,4 @@ namespace System.Net.Security base.Dispose(disposing); } } -} +}
\ No newline at end of file diff --git a/src/Native/Unix/System.Net.Security.Native/pal_gssapi.c b/src/Native/Unix/System.Net.Security.Native/pal_gssapi.c index a97bce4919..97a7e0e75e 100644 --- a/src/Native/Unix/System.Net.Security.Native/pal_gssapi.c +++ b/src/Native/Unix/System.Net.Security.Native/pal_gssapi.c @@ -111,7 +111,7 @@ static uint32_t NetSecurityNative_DisplayStatus(uint32_t* minorStatus, assert(minorStatus != NULL); assert(outBuffer != NULL); - uint32_t messageContext; + uint32_t messageContext = 0; // Must initialize to 0 before calling gss_display_status. GssBuffer gssBuffer = {.length = 0, .value = NULL}; uint32_t majorStatus = gss_display_status(minorStatus, statusValue, statusType, GSS_C_NO_OID, &messageContext, &gssBuffer); @@ -154,19 +154,36 @@ uint32_t NetSecurityNative_ImportPrincipalName(uint32_t* minorStatus, assert(outputName != NULL); assert(*outputName == NULL); - gss_OID nameType; - - if (strchr(inputName, '/') != NULL) + // Principal name will usually be in the form SERVICE/HOST. But SPNEGO protocol prefers + // GSS_C_NT_HOSTBASED_SERVICE format. That format uses '@' separator instead of '/' between + // service name and host name. So convert input string into that format. + char* ptrSlash = memchr(inputName, '/', inputNameLen); + char* inputNameCopy = NULL; + if (ptrSlash != NULL) { - nameType = GSS_KRB5_NT_PRINCIPAL_NAME; + inputNameCopy = (char*) malloc(inputNameLen); + if (inputNameCopy != NULL) + { + memcpy(inputNameCopy, inputName, inputNameLen); + inputNameCopy[ptrSlash - inputName] = '@'; + inputName = inputNameCopy; + } + else + { + *minorStatus = 0; + return GSS_S_BAD_NAME; + } } - else + + GssBuffer inputNameBuffer = {.length = inputNameLen, .value = inputName}; + uint32_t result = gss_import_name(minorStatus, &inputNameBuffer, GSS_C_NT_HOSTBASED_SERVICE, outputName); + + if (inputNameCopy != NULL) { - nameType = GSS_C_NT_HOSTBASED_SERVICE; + free(inputNameCopy); } - GssBuffer inputNameBuffer = {.length = inputNameLen, .value = inputName}; - return gss_import_name(minorStatus, &inputNameBuffer, nameType, outputName); + return result; } uint32_t NetSecurityNative_InitSecContext(uint32_t* minorStatus, @@ -181,6 +198,35 @@ uint32_t NetSecurityNative_InitSecContext(uint32_t* minorStatus, uint32_t* retFlags, int32_t* isNtlmUsed) { + return NetSecurityNative_InitSecContextEx(minorStatus, + claimantCredHandle, + contextHandle, + isNtlm, + NULL, + 0, + targetName, + reqFlags, + inputBytes, + inputLength, + outBuffer, + retFlags, + isNtlmUsed); +} + +uint32_t NetSecurityNative_InitSecContextEx(uint32_t* minorStatus, + GssCredId* claimantCredHandle, + GssCtxId** contextHandle, + uint32_t isNtlm, + void* cbt, + int32_t cbtSize, + GssName* targetName, + uint32_t reqFlags, + uint8_t* inputBytes, + uint32_t inputLength, + PAL_GssBuffer* outBuffer, + uint32_t* retFlags, + int32_t* isNtlmUsed) +{ assert(minorStatus != NULL); assert(contextHandle != NULL); assert(isNtlm == 0 || isNtlm == 1); @@ -189,12 +235,13 @@ uint32_t NetSecurityNative_InitSecContext(uint32_t* minorStatus, assert(outBuffer != NULL); assert(retFlags != NULL); assert(isNtlmUsed != NULL); - assert(inputBytes != NULL || inputLength == 0); + assert(cbt != NULL || cbtSize == 0); // Note: claimantCredHandle can be null // Note: *contextHandle is null only in the first call and non-null in the subsequent calls #if HAVE_GSS_SPNEGO_MECHANISM + gss_OID krbMech = GSS_KRB5_MECHANISM; gss_OID desiredMech; if (isNtlm) { @@ -204,9 +251,8 @@ uint32_t NetSecurityNative_InitSecContext(uint32_t* minorStatus, { desiredMech = GSS_SPNEGO_MECHANISM; } - - gss_OID krbMech = GSS_KRB5_MECHANISM; #else + gss_OID krbMech = (gss_OID)(unsigned long)gss_mech_krb5; gss_OID_desc gss_mech_OID_desc; if (isNtlm) { @@ -218,14 +264,20 @@ uint32_t NetSecurityNative_InitSecContext(uint32_t* minorStatus, } gss_OID desiredMech = &gss_mech_OID_desc; - gss_OID krbMech = gss_mech_krb5; #endif - *isNtlmUsed = 1; GssBuffer inputToken = {.length = inputLength, .value = inputBytes}; GssBuffer gssBuffer = {.length = 0, .value = NULL}; gss_OID_desc* outmech; + struct gss_channel_bindings_struct gssCbt; + if (cbt != NULL) + { + memset(&gssCbt, 0, sizeof(struct gss_channel_bindings_struct)); + gssCbt.application_data.length = (size_t)cbtSize; + gssCbt.application_data.value = cbt; + } + uint32_t majorStatus = gss_init_sec_context(minorStatus, claimantCredHandle, contextHandle, @@ -233,18 +285,14 @@ uint32_t NetSecurityNative_InitSecContext(uint32_t* minorStatus, desiredMech, reqFlags, 0, - GSS_C_NO_CHANNEL_BINDINGS, + (cbt != NULL) ? &gssCbt : GSS_C_NO_CHANNEL_BINDINGS, &inputToken, &outmech, &gssBuffer, retFlags, NULL); - // Outmech can be null when gssntlmssp lib uses NTLM mechanism - if (outmech != NULL && gss_oid_equal(outmech, krbMech) != 0) - { - *isNtlmUsed = 0; - } + *isNtlmUsed = (isNtlm || majorStatus != GSS_S_COMPLETE || gss_oid_equal(outmech, krbMech) == 0) ? 1 : 0; NetSecurityNative_MoveBuffer(&gssBuffer, outBuffer); return majorStatus; @@ -254,7 +302,8 @@ uint32_t NetSecurityNative_AcceptSecContext(uint32_t* minorStatus, GssCtxId** contextHandle, uint8_t* inputBytes, uint32_t inputLength, - PAL_GssBuffer* outBuffer) + PAL_GssBuffer* outBuffer, + uint32_t* retFlags) { assert(minorStatus != NULL); assert(contextHandle != NULL); @@ -273,7 +322,7 @@ uint32_t NetSecurityNative_AcceptSecContext(uint32_t* minorStatus, NULL, NULL, &gssBuffer, - 0, + retFlags, NULL, NULL); @@ -281,6 +330,44 @@ uint32_t NetSecurityNative_AcceptSecContext(uint32_t* minorStatus, return majorStatus; } +uint32_t NetSecurityNative_GetUser(uint32_t* minorStatus, + GssCtxId* contextHandle, + PAL_GssBuffer* outBuffer) +{ + assert(minorStatus != NULL); + assert(contextHandle != NULL); + assert(outBuffer != NULL); + + gss_name_t srcName = GSS_C_NO_NAME; + + uint32_t majorStatus = gss_inquire_context(minorStatus, + contextHandle, + &srcName, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); + + if (majorStatus == GSS_S_COMPLETE) + { + GssBuffer gssBuffer = {.length = 0, .value = NULL}; + majorStatus = gss_display_name(minorStatus, srcName, &gssBuffer, NULL); + if (majorStatus == GSS_S_COMPLETE) + { + NetSecurityNative_MoveBuffer(&gssBuffer, outBuffer); + } + } + + if (srcName != NULL) + { + majorStatus = gss_release_name(minorStatus, &srcName); + } + + return majorStatus; +} + uint32_t NetSecurityNative_ReleaseCred(uint32_t* minorStatus, GssCredId** credHandle) { assert(minorStatus != NULL); @@ -416,4 +503,37 @@ uint32_t NetSecurityNative_InitiateCredWithPassword(uint32_t* minorStatus, { return NetSecurityNative_AcquireCredWithPassword( minorStatus, isNtlm, desiredName, password, passwdLen, GSS_C_INITIATE, outputCredHandle); +} + +uint32_t NetSecurityNative_IsNtlmInstalled() +{ +#if HAVE_GSS_SPNEGO_MECHANISM + gss_OID ntlmOid = GSS_NTLM_MECHANISM; +#else + gss_OID ntlmOid = &gss_mech_ntlm_OID_desc; +#endif + + uint32_t majorStatus; + uint32_t minorStatus; + gss_OID_set mechSet; + gss_OID_desc oid; + uint32_t foundNtlm = 0; + + majorStatus = gss_indicate_mechs(&minorStatus, &mechSet); + if (majorStatus == GSS_S_COMPLETE) + { + for (size_t i = 0; i < mechSet->count; i++) + { + oid = mechSet->elements[i]; + if ((oid.length == ntlmOid->length) && (memcmp(oid.elements, ntlmOid->elements, oid.length) == 0)) + { + foundNtlm = 1; + break; + } + } + + gss_release_oid_set(&minorStatus, &mechSet); + } + + return foundNtlm; }
\ No newline at end of file diff --git a/src/Native/Unix/System.Net.Security.Native/pal_gssapi.h b/src/Native/Unix/System.Net.Security.Native/pal_gssapi.h index 91071cf56e..5b951e7d21 100644 --- a/src/Native/Unix/System.Net.Security.Native/pal_gssapi.h +++ b/src/Native/Unix/System.Net.Security.Native/pal_gssapi.h @@ -115,6 +115,20 @@ DLLEXPORT uint32_t NetSecurityNative_InitSecContext(uint32_t* minorStatus, uint32_t* retFlags, int32_t* isNtlmUsed); +DLLEXPORT uint32_t NetSecurityNative_InitSecContextEx(uint32_t* minorStatus, + GssCredId* claimantCredHandle, + GssCtxId** contextHandle, + uint32_t isNtlm, + void* cbt, + int32_t cbtSize, + GssName* targetName, + uint32_t reqFlags, + uint8_t* inputBytes, + uint32_t inputLength, + PAL_GssBuffer* outBuffer, + uint32_t* retFlags, + int32_t* isNtlmUsed); + /* Shims the gss_accept_sec_context method. */ @@ -122,7 +136,8 @@ DLLEXPORT uint32_t NetSecurityNative_AcceptSecContext(uint32_t* minorStatus, GssCtxId** contextHandle, uint8_t* inputBytes, uint32_t inputLength, - PAL_GssBuffer* outBuffer); + PAL_GssBuffer* outBuffer, + uint32_t* retFlags); /* @@ -159,4 +174,16 @@ DLLEXPORT uint32_t NetSecurityNative_InitiateCredWithPassword(uint32_t* minorSta GssName* desiredName, char* password, uint32_t passwdLen, - GssCredId** outputCredHandle);
\ No newline at end of file + GssCredId** outputCredHandle); + +/* +Shims the gss_indicate_mechs method to detect if NTLM mech is installed. +*/ +DLLEXPORT uint32_t NetSecurityNative_IsNtlmInstalled(void); + +/* +Shims gss_inquire_context and gss_display_name to get the remote user principal name. +*/ +DLLEXPORT uint32_t NetSecurityNative_GetUser(uint32_t* minorStatus, + GssCtxId* contextHandle, + PAL_GssBuffer* outBuffer);
\ No newline at end of file diff --git a/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs index 4c6d148c70..5f424ff2e9 100644 --- a/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs +++ b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Net; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; @@ -76,7 +77,44 @@ namespace System.Net.Http string challengeData = challenge.ChallengeData; - string spn = "HTTP/" + authUri.IdnHost; + // Calculate SPN (Service Principal Name) using the host name of the request. + // Use the request's 'Host' header if available. Otherwise, use the request uri. + // Ignore the 'Host' header if this is proxy authentication since we need to use + // the host name of the proxy itself for SPN calculation. + string hostName; + if (!isProxyAuth && request.HasHeaders && request.Headers.Host != null) + { + // Use the host name without any normalization. + hostName = request.Headers.Host; + if (NetEventSource.IsEnabled) + { + NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Host: {hostName}"); + } + } + else + { + // Need to use FQDN normalized host so that CNAME's are traversed. + // Use DNS to do the forward lookup to an A (host) record. + // But skip DNS lookup on IP literals. Otherwise, we would end up + // doing an unintended reverse DNS lookup. + UriHostNameType hnt = authUri.HostNameType; + if (hnt == UriHostNameType.IPv6 || hnt == UriHostNameType.IPv4) + { + hostName = authUri.IdnHost; + } + else + { + IPHostEntry result = await Dns.GetHostEntryAsync(authUri.IdnHost).ConfigureAwait(false); + hostName = result.HostName; + } + } + + string spn = "HTTP/" + hostName; + if (NetEventSource.IsEnabled) + { + NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, SPN: {spn}"); + } + ChannelBinding channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint); NTAuthentication authContext = new NTAuthentication(isServer:false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection, channelBinding); try @@ -135,4 +173,3 @@ namespace System.Net.Http } } } - |