From c4a4aa7badbc6cdb253000d58128f9dafab4b3b4 Mon Sep 17 00:00:00 2001 From: Hemanth Kapila Date: Mon, 18 Jan 2016 13:39:51 +0530 Subject: [PATCH 1/6] Interop Layer for supporting xplat NegotiateStream Introducing native shims for libgssapi in order to support NegotiateStream --- .../src/Interop/Unix/Interop.Libraries.cs | 1 + .../System.Net.Security.Native/Interop.Gss.cs | 336 ++++++++++++++++++ .../src/Interop/Unix/libgss/Interop.GssApi.cs | 162 +++++++++ .../Unix/libgss/Interop.GssApiException.cs | 93 +++++ .../System.Net.Security.Native/CMakeLists.txt | 32 ++ .../System.Net.Security.Native/pal_gssapi.cpp | 111 ++++++ .../System.Net.Security.Native/pal_gssapi.h | 89 +++++ .../src/Resources/Strings.resx | 19 +- 8 files changed, 841 insertions(+), 2 deletions(-) create mode 100644 src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Gss.cs create mode 100644 src/Common/src/Interop/Unix/libgss/Interop.GssApi.cs create mode 100644 src/Common/src/Interop/Unix/libgss/Interop.GssApiException.cs create mode 100644 src/Native/System.Net.Security.Native/CMakeLists.txt create mode 100644 src/Native/System.Net.Security.Native/pal_gssapi.cpp create mode 100644 src/Native/System.Net.Security.Native/pal_gssapi.h diff --git a/src/Common/src/Interop/Unix/Interop.Libraries.cs b/src/Common/src/Interop/Unix/Interop.Libraries.cs index 8e5de4df736b..fc0ca0bc982a 100644 --- a/src/Common/src/Interop/Unix/Interop.Libraries.cs +++ b/src/Common/src/Interop/Unix/Interop.Libraries.cs @@ -8,6 +8,7 @@ private static partial class Libraries // Shims internal const string SystemNative = "System.Native"; internal const string HttpNative = "System.Net.Http.Native"; + internal const string SecurityNative = "System.Net.Security.Native"; internal const string CryptoNative = "System.Security.Cryptography.Native"; internal const string GlobalizationNative = "System.Globalization.Native"; internal const string CompressionNative = "System.IO.Compression.Native"; diff --git a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Gss.cs b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Gss.cs new file mode 100644 index 000000000000..c1b3090c5c73 --- /dev/null +++ b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Gss.cs @@ -0,0 +1,336 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Win32.SafeHandles; +using size_t = System.IntPtr; + +internal static partial class Interop +{ + internal static partial class libgssapi + { + [DllImport(Interop.Libraries.SecurityNative)] + internal static extern Status GssReleaseBuffer( + out Status minorStatus, + ref gss_buffer_desc buffer); + + [DllImport(Interop.Libraries.SecurityNative)] + internal static extern Status GssDisplayStatus( + out Status minorStatus, + Status statusValue, + bool isGssMechCode, + SafeGssBufferHandle statusString); + + [DllImport(Interop.Libraries.SecurityNative, CharSet = CharSet.Ansi)] + internal static extern Status GssImportName( + out Status minorStatus, + string inputName, + bool isUser, + out SafeGssNameHandle outputName); + + [DllImport(Interop.Libraries.SecurityNative)] + internal static extern Status GssReleaseName( + out Status minorStatus, + ref IntPtr inputName); + + [DllImport(Interop.Libraries.SecurityNative)] + internal static extern Status GssAcquireCredSpNego( + out Status minorStatus, + SafeGssNameHandle desiredName, + bool isInitiate, + out SafeGssCredHandle outputCredHandle); + + [DllImport(Interop.Libraries.SecurityNative, CharSet = CharSet.Ansi)] + internal static extern Status GssAcquireCredWithPassword( + out Status minorStatus, + SafeGssNameHandle desiredName, + string password, + bool isInitiate, + out SafeGssCredHandle outputCredHandle); + + [DllImport(Interop.Libraries.SecurityNative)] + internal static extern Status GssReleaseCred( + out Status minorStatus, + ref IntPtr credHandle); + + [DllImport(Interop.Libraries.SecurityNative)] + internal static extern Status GssInitSecContext( + out Status minorStatus, + SafeGssCredHandle initiatorCredHandle, + ref SafeGssContextHandle contextHandle, + bool isNtlm, + SafeGssNameHandle targetName, + uint reqFlags, + byte[] inputBytes, + int inputLength, + SafeGssBufferHandle outputToken, + out uint retFlags); + + [DllImport(Interop.Libraries.SecurityNative)] + internal static extern Status GssAcceptSecContext( + out Status minorStatus, + ref SafeGssContextHandle contextHandle, + SafeGssCredHandle initiatorCredHandle, + SafeGssBufferHandle inputToken, + SafeGssBufferHandle outputToken, + out uint retFlags); + + [DllImport(Interop.Libraries.SecurityNative)] + internal static extern Status GssDeleteSecContext( + out Status minorStatus, + ref IntPtr contextHandle); + + [DllImport(Interop.Libraries.SecurityNative)] + internal static extern Status GssWrap( + out Status minorStatus, + SafeGssContextHandle contextHandle, + bool isEncrypt, + byte[] inputBytes, + int offset, + int count, + SafeGssBufferHandle outputMessageBuffer); + + [DllImport(Interop.Libraries.SecurityNative)] + internal static extern Status GssUnwrap( + out Status minorStatus, + SafeGssContextHandle contextHandle, + byte[] inputBytes, + int offset, + int count, + SafeGssBufferHandle outputMessageBuffer); + + [DllImport(Interop.Libraries.SecurityNative)] + internal static extern Status GssInquireSourceName( + out Status minorStatus, + SafeGssContextHandle contextHandle, + out SafeGssNameHandle srcName); + + [DllImport(Interop.Libraries.SecurityNative)] + internal static extern Status GssDisplayName( + out Status minorStatus, + SafeGssNameHandle inputName, + SafeGssBufferHandle outputNameBuffer); + + [StructLayout(LayoutKind.Sequential)] + internal struct gss_buffer_desc + { + internal size_t length; + internal IntPtr value; + } + + internal enum Status : uint + { + GSS_S_COMPLETE = 0, + GSS_S_CONTINUE_NEEDED = 1 + } + + [FlagsAttribute] + internal enum GssFlags : uint + { + GSS_C_DELEG_FLAG = 1, + GSS_C_MUTUAL_FLAG = 2, + GSS_C_REPLAY_FLAG = 4, + GSS_C_SEQUENCE_FLAG = 8, + GSS_C_CONF_FLAG = 16, + GSS_C_INTEG_FLAG = 32, + GSS_C_ANON_FLAG = 64, + GSS_C_PROT_READY_FLAG = 128, + GSS_C_TRANS_FLAG = 256, + GSS_C_DCE_STYLE = 4096, + GSS_C_IDENTIFY_FLAG = 8192, + GSS_C_EXTENDED_ERROR_FLAG = 16384, + GSS_C_DELEG_POLICY_FLAG = 32768 + } + } +} + +namespace Microsoft.Win32.SafeHandles +{ + /// + /// Wrapper around an output gss_buffer_desc* + /// + internal sealed class SafeGssBufferHandle : SafeHandle + { + private GCHandle _gch; + + // Return the buffer size + public int Length + { + get + { + if (IsInvalid) + { + return 0; + } + return (int)((Interop.libgssapi.gss_buffer_desc)_gch.Target).length; + } + } + + // Return a pointer to where data resides + public IntPtr Value + { + get + { + if (IsInvalid) + { + return IntPtr.Zero; + } + return ((Interop.libgssapi.gss_buffer_desc)_gch.Target).value; + } + } + + public SafeGssBufferHandle() : base(IntPtr.Zero, true) + { + Interop.libgssapi.gss_buffer_desc buffer = new Interop.libgssapi.gss_buffer_desc + { + length = (size_t)0, + value = IntPtr.Zero, + }; + + _gch = GCHandle.Alloc(buffer, GCHandleType.Pinned); + handle = _gch.AddrOfPinnedObject(); + } + + public override bool IsInvalid + { + get { return handle == IntPtr.Zero; } + } + + // Note that _value should never be freed directly. For input + // buffer, it is owned by the caller and for output buffer, + // it is owned by libgssapi + protected override bool ReleaseHandle() + { + Interop.libgssapi.gss_buffer_desc buffer = (Interop.libgssapi.gss_buffer_desc) _gch.Target; + if (buffer.value != IntPtr.Zero) + { + Interop.libgssapi.Status minorStatus; + Interop.libgssapi.Status status = Interop.libgssapi.GssReleaseBuffer(out minorStatus, ref buffer); + Interop.libgssapi.GssApiException.AssertOrThrowIfError("GssReleaseBuffer failed", status, minorStatus); + } + + _gch.Free(); + SetHandle(IntPtr.Zero); + return true; + } + } + + /// + /// Wrapper around a gss_name_t_desc* + /// + internal sealed class SafeGssNameHandle : SafeHandle + { + public static SafeGssNameHandle Create(string name, bool isUser) + { + Debug.Assert(!String.IsNullOrEmpty(name), "Invalid name passed to SafeGssNameHandle create"); + SafeGssNameHandle retHandle; + Interop.libgssapi.Status minorStatus; + Interop.libgssapi.Status status = Interop.libgssapi.GssImportName(out minorStatus, name, isUser, out retHandle); + if (status != Interop.libgssapi.Status.GSS_S_COMPLETE) + { + throw Interop.libgssapi.GssApiException.Create(status, minorStatus); + } + + return retHandle; + } + + public override bool IsInvalid + { + get { return handle == IntPtr.Zero; } + } + + protected override bool ReleaseHandle() + { + Interop.libgssapi.Status minorStatus; + Interop.libgssapi.Status status = Interop.libgssapi.GssReleaseName(out minorStatus, ref handle); + Interop.libgssapi.GssApiException.AssertOrThrowIfError("GssReleaseName failed", status, minorStatus); + SetHandle(IntPtr.Zero); + return true; + } + + private SafeGssNameHandle() + : base(IntPtr.Zero, true) + { + } + } + + /// + /// Wrapper around a gss_cred_id_t_desc_struct* + /// + internal class SafeGssCredHandle : SafeHandle + { + public static SafeGssCredHandle Create(string username, string password, string domain) + { + SafeGssCredHandle retHandle = null; + + // Empty username is OK if Kerberos ticket was already obtained + if (!String.IsNullOrEmpty(username)) + { + using (SafeGssNameHandle userHandle = SafeGssNameHandle.Create(username, true)) + { + Interop.libgssapi.Status status; + Interop.libgssapi.Status minorStatus; + if (String.IsNullOrEmpty(password)) + { + status = Interop.libgssapi.GssAcquireCredSpNego(out minorStatus, userHandle, true, out retHandle); + } + else + { + status = Interop.libgssapi.GssAcquireCredWithPassword(out minorStatus, userHandle, password, true, out retHandle); + } + + if (status != Interop.libgssapi.Status.GSS_S_COMPLETE) + { + throw Interop.libgssapi.GssApiException.Create(status, minorStatus); + } + } + } + + return retHandle; + } + + private SafeGssCredHandle() + : base(IntPtr.Zero, true) + { + } + + public override bool IsInvalid + { + get { return handle == IntPtr.Zero; } + } + + protected override bool ReleaseHandle() + { + Interop.libgssapi.Status minorStatus; + Interop.libgssapi.Status status = Interop.libgssapi.GssReleaseCred(out minorStatus, ref handle); + Interop.libgssapi.GssApiException.AssertOrThrowIfError("GssReleaseCred failed", status, minorStatus); + SetHandle(IntPtr.Zero); + return true; + } + } + + internal sealed class SafeGssContextHandle : SafeHandle + { + public SafeGssContextHandle() + : base(IntPtr.Zero, true) + { + } + + public override bool IsInvalid + { + get { return handle == IntPtr.Zero; } + } + + protected override bool ReleaseHandle() + { + Interop.libgssapi.Status minorStatus; + Interop.libgssapi.Status status = Interop.libgssapi.GssDeleteSecContext(out minorStatus, ref handle); + Interop.libgssapi.GssApiException.AssertOrThrowIfError("GssDeleteSecContext failed", status, minorStatus); + SetHandle(IntPtr.Zero); + return true; + } + } +} diff --git a/src/Common/src/Interop/Unix/libgss/Interop.GssApi.cs b/src/Common/src/Interop/Unix/libgss/Interop.GssApi.cs new file mode 100644 index 000000000000..6f56f129bd39 --- /dev/null +++ b/src/Common/src/Interop/Unix/libgss/Interop.GssApi.cs @@ -0,0 +1,162 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license 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.libgssapi.GssFlags inFlags, + byte[] buffer, + out byte[] outputBuffer, + out uint outFlags) + { + outputBuffer = null; + outFlags = 0; + + if (context == null) + { + context = new SafeGssContextHandle(); + } + + using (SafeGssBufferHandle outputToken = new SafeGssBufferHandle()) + { + libgssapi.Status status; + libgssapi.Status minorStatus; + + status = libgssapi.GssInitSecContext( + out minorStatus, + credential, + ref context, + isNtlm, + targetName, + (uint)inFlags, + buffer, + (buffer == null) ? 0 : buffer.Length, + outputToken, + out outFlags); + + if ((status != libgssapi.Status.GSS_S_COMPLETE) && (status != libgssapi.Status.GSS_S_CONTINUE_NEEDED)) + { + throw libgssapi.GssApiException.Create(SR.net_context_establishment_failed, status, minorStatus); + } + + outputBuffer = new byte[outputToken.Length]; // Always return non-null + if (outputToken.Length > 0) + { + Marshal.Copy(outputToken.Value, outputBuffer, 0, outputToken.Length); + } + return (status == libgssapi.Status.GSS_S_COMPLETE) ? true : false; + } + } + + internal static int Encrypt( + SafeGssContextHandle context, + bool encrypt, + byte[] buffer, + int offset, + int count, + out byte[] outputBuffer) + { + outputBuffer = null; + 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"); + + using (SafeGssBufferHandle outputToken = new SafeGssBufferHandle()) + { + libgssapi.Status minorStatus; + libgssapi.Status status = libgssapi.GssWrap(out minorStatus, context, encrypt, buffer, offset, count, outputToken); + if (status != libgssapi.Status.GSS_S_COMPLETE) + { + throw libgssapi.GssApiException.Create(SR.net_context_wrap_failed, status, minorStatus); + } + + outputBuffer = new byte[outputToken.Length]; // Always return non-null + if (outputToken.Length > 0) + { + Marshal.Copy(outputToken.Value, outputBuffer, 0, outputToken.Length); + } + + return outputBuffer.Length; + } + } + + 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"); + + using (SafeGssBufferHandle outputToken = new SafeGssBufferHandle()) + { + libgssapi.Status minorStatus; + libgssapi.Status status = libgssapi.GssUnwrap(out minorStatus, context, buffer, offset, count, outputToken); + if (status != libgssapi.Status.GSS_S_COMPLETE) + { + throw libgssapi.GssApiException.Create(SR.net_context_unwrap_failed, status, minorStatus); + } + + int length = buffer.Length - offset; + if (outputToken.Length > length) + { + throw libgssapi.GssApiException.Create(SR.Format(SR.net_context_buffer_too_small, outputToken.Length, length)); + } + + if (outputToken.Length > 0) + { + Marshal.Copy(outputToken.Value, buffer, offset, outputToken.Length); + } + + return outputToken.Length; + } + } + + internal static string GetSourceName(SafeGssContextHandle context) + { + libgssapi.Status minorStatus; + SafeGssNameHandle sourceName; + libgssapi.Status status = libgssapi.GssInquireSourceName( + out minorStatus, + context, + out sourceName); + + if (status != libgssapi.Status.GSS_S_COMPLETE) + { + throw libgssapi.GssApiException.Create(status, minorStatus); + } + + using (sourceName) + using (SafeGssBufferHandle outputBuffer = new SafeGssBufferHandle()) + { + status = libgssapi.GssDisplayName(out minorStatus, sourceName, outputBuffer); + if (status != libgssapi.Status.GSS_S_COMPLETE) + { + throw libgssapi.GssApiException.Create(status, minorStatus); + } + + // String may not be NULL terminated so PtrToStringAnsi cannot be used + unsafe + { + return Encoding.UTF8.GetString((byte*)outputBuffer.Value.ToPointer(), outputBuffer.Length); + } + } + } + } +} + + diff --git a/src/Common/src/Interop/Unix/libgss/Interop.GssApiException.cs b/src/Common/src/Interop/Unix/libgss/Interop.GssApiException.cs new file mode 100644 index 000000000000..63af4bf30bb3 --- /dev/null +++ b/src/Common/src/Interop/Unix/libgss/Interop.GssApiException.cs @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class libgssapi + { + internal sealed class GssApiException : Exception + { + private Status _minorStatus; + + public Status MinorStatus + { + get { return _minorStatus; } + } + + public GssApiException(string message) : base(message) + { + } + + public GssApiException(Status majorStatus, Status minorStatus) + : this(SR.Format(SR.net_gssapi_operation_failed, majorStatus, minorStatus), majorStatus, minorStatus) + { + } + + public GssApiException(string message, Status majorStatus, Status minorStatus) + : this(message) + { + HResult = (int)majorStatus; + _minorStatus = minorStatus; + } + + + public static GssApiException Create(string message) + { + return new GssApiException(message); + } + + public static GssApiException Create(Status majorStatus, Status minorStatus) + { + return new GssApiException(majorStatus, minorStatus); + } + + public static GssApiException Create(string message, Status majorStatus, Status minorStatus) + { + return new GssApiException(SR.Format(message, majorStatus, minorStatus), majorStatus, minorStatus); + } + + public static void AssertOrThrowIfError(string message, Status majorStatus, Status minorStatus) + { + if (majorStatus != Status.GSS_S_COMPLETE) + { + GssApiException ex = Create(majorStatus, minorStatus); + Debug.Fail(message + ": " + ex); + throw ex; + } + } + +#if DEBUG + public override string ToString() + { + return Message + "\n GSSAPI status: " + GetGssApiDisplayStatus((Status)HResult, _minorStatus); + } + + private static string GetGssApiDisplayStatus(Status majorStatus, Status minorStatus) + { + Tuple[] statusArr = { Tuple.Create(majorStatus, false), Tuple.Create(minorStatus, true) }; + int length = statusArr.Length; + string[] msgStrings = new string[length]; + + for (int i = 0; i < length; i++) + { + using (SafeGssBufferHandle msgBuffer = new SafeGssBufferHandle()) + { + Interop.libgssapi.Status minStat; + if (Status.GSS_S_COMPLETE != GssDisplayStatus(out minStat, statusArr[i].Item1, statusArr[i].Item2, msgBuffer)) + { + continue; + } + msgStrings[i] = Marshal.PtrToStringAnsi(msgBuffer.Value); + } + } + return msgStrings[0] + " (" + msgStrings[1] + ") \nStatus: " + majorStatus.ToString("x8") + " (" + minorStatus.ToString("x8") + ")"; + } + } + } +#endif +} diff --git a/src/Native/System.Net.Security.Native/CMakeLists.txt b/src/Native/System.Net.Security.Native/CMakeLists.txt new file mode 100644 index 000000000000..16253dfe8093 --- /dev/null +++ b/src/Native/System.Net.Security.Native/CMakeLists.txt @@ -0,0 +1,32 @@ +project(System.Net.Security.Native) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +add_definitions(-DPIC=1) + +if (APPLE) + find_library(LIBGSS NAMES GSS) + add_definitions(-DGSSFW) +else() + find_library(LIBGSS NAMES gssapi_krb5) +endif() + +if(LIBGSS STREQUAL LIBGSS-NOTFOUND) + message(SEND_ERROR "!!! Cannot find libgssapi_krb5 and System.Net.Security.Native cannot build without it. Try installing libkrb5-dev (or the appropriate package for your platform) !!!") + return() +endif() + +set(NATIVEGSS_SOURCES + pal_gssapi.cpp +) + +add_library(System.Net.Security.Native + SHARED + ${NATIVEGSS_SOURCES} +) + +target_link_libraries(System.Net.Security.Native + ${LIBGSS} +) + +install (TARGETS System.Net.Security.Native DESTINATION .) diff --git a/src/Native/System.Net.Security.Native/pal_gssapi.cpp b/src/Native/System.Net.Security.Native/pal_gssapi.cpp new file mode 100644 index 000000000000..9b22c1704ba5 --- /dev/null +++ b/src/Native/System.Net.Security.Native/pal_gssapi.cpp @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "pal_gssapi.h" +#include +#include + +static_assert(PAL_GSS_COMPLETE == GSS_S_COMPLETE, ""); +static_assert(PAL_GSS_CONTINUE_NEEDED == GSS_S_CONTINUE_NEEDED, ""); + +static char gss_mech_value[] = "\x2b\x06\x01\x05\x05\x02"; + +static gss_OID_desc gss_mech_spnego_OID_desc = {6, static_cast(gss_mech_value)}; + +static gss_OID gss_mech_spnego_OID = &gss_mech_spnego_OID_desc; + +static gss_OID_set_desc gss_mech_spnego_OID_set_desc = {1, &gss_mech_spnego_OID_desc}; + +static gss_OID_set gss_mech_spnego_OID_set = &gss_mech_spnego_OID_set_desc; + +extern "C" uint32_t GssAcquireCredSpNego(uint32_t* minorStatus, gss_name_t desiredName, bool isInitiate, gss_cred_id_t* outputCredHandle) +{ + gss_cred_usage_t credUsage = isInitiate ? GSS_C_INITIATE : GSS_C_ACCEPT; + return gss_acquire_cred(minorStatus, desiredName, 0, gss_mech_spnego_OID_set, credUsage, outputCredHandle, NULL, NULL); +} + +extern "C" uint32_t GssDeleteSecContext(uint32_t* minorStatus, gss_ctx_id_t* contextHandle) +{ + return gss_delete_sec_context(minorStatus, contextHandle, GSS_C_NO_BUFFER); +} + +extern "C" uint32_t GssDisplayName(uint32_t* minorStatus, gss_name_t inputName, gss_buffer_t outputNameBuffer) +{ + return gss_display_name(minorStatus, inputName, outputNameBuffer, NULL); +} + +extern "C" uint32_t GssDisplayStatus(uint32_t* minorStatus, uint32_t statusValue, bool isGssMechCode, gss_buffer_t statusString) +{ + int statusType = isGssMechCode ? GSS_C_MECH_CODE : GSS_C_GSS_CODE; + return gss_display_status(minorStatus, statusValue, statusType, GSS_C_NO_OID, NULL, statusString); +} + +extern "C" uint32_t GssImportName(uint32_t* minorStatus, char* inputName, bool isUser, gss_name_t* outputName) +{ + gss_buffer_desc inputNameBuffer {strlen(inputName), inputName}; + gss_OID nameType = isUser ? const_cast(GSS_C_NT_USER_NAME) : const_cast(GSS_KRB5_NT_PRINCIPAL_NAME); + return gss_import_name(minorStatus, &inputNameBuffer, nameType, outputName); +} + +extern "C" uint32_t GssInitSecContext(uint32_t* minorStatus, gss_cred_id_t claimantCredHandle, gss_ctx_id_t* contextHandle, + bool isNtlm, gss_name_t targetName, uint32_t reqFlags, uint8_t* inputBytes, int32_t inputLength, gss_buffer_t outputToken, uint32_t* retFlags) +{ +#ifdef GSSFW + gss_OID desiredMech = isNtlm ? const_cast(GSS_NTLM_MECHANISM) : gss_mech_spnego_OID; +#else + assert(!isNtlm && "NTLM is not supported by MIT libgssapi_krb5"); + gss_OID desiredMech = gss_mech_spnego_OID; +#endif + + gss_buffer_desc inputToken {static_cast(inputLength), inputBytes}; + return gss_init_sec_context(minorStatus, claimantCredHandle, contextHandle, targetName, desiredMech, reqFlags, + 0, GSS_C_NO_CHANNEL_BINDINGS, &inputToken, NULL, outputToken, retFlags, NULL); +} + +extern "C" uint32_t GssInquireSourceName(uint32_t* minorStatus, gss_ctx_id_t contextHandle, gss_name_t* srcName) +{ + return gss_inquire_context(minorStatus, contextHandle, srcName, NULL, NULL, NULL, NULL, NULL, NULL); +} + +extern "C" uint32_t GssReleaseCred(uint32_t* minorStatus, gss_cred_id_t* credHandle) +{ + return gss_release_cred(minorStatus, credHandle); +} + +extern "C" uint32_t GssReleaseBuffer(uint32_t* minor_status, gss_buffer_t buffer) +{ + return gss_release_buffer(minor_status, buffer); +} + +extern "C" uint32_t GssReleaseName(uint32_t* minorStatus, gss_name_t* inputName) +{ + return gss_release_name(minorStatus, inputName); +} + +extern "C" uint32_t GssWrap(uint32_t* minorStatus, gss_ctx_id_t contextHandle, bool isEncrypt, + uint8_t* inputBytes, int32_t offset, int32_t count, gss_buffer_t outputMessageBuffer) +{ + gss_buffer_desc inputMessageBuffer {static_cast(count), inputBytes + offset}; + int confState; + int confReqFlag = isEncrypt ? 1 : 0; + uint32_t retVal = gss_wrap(minorStatus, contextHandle, confReqFlag, GSS_C_QOP_DEFAULT, &inputMessageBuffer, &confState, outputMessageBuffer); + assert((confState == 0) == (confReqFlag == 0)); + return retVal; +} + +extern "C" uint32_t GssUnwrap(uint32_t* minorStatus, gss_ctx_id_t contextHandle, + uint8_t* inputBytes, int32_t offset, int32_t count, gss_buffer_t outputMessageBuffer) +{ + gss_buffer_desc inputMessageBuffer {static_cast(count), inputBytes + offset}; + return gss_unwrap(minorStatus, contextHandle, &inputMessageBuffer, outputMessageBuffer, NULL, NULL); +} + +extern "C" uint32_t GssAcquireCredWithPassword(uint32_t* minorStatus, const gss_name_t desiredName, char* password, bool isInitiate, + gss_cred_id_t* outputCredHandle) +{ + gss_cred_usage_t credUsage = isInitiate ? GSS_C_INITIATE : GSS_C_ACCEPT; + gss_buffer_desc passwordBuffer {strlen(password), password}; + + return gss_acquire_cred_with_password(minorStatus, desiredName, &passwordBuffer, 0, NULL, + credUsage, outputCredHandle, NULL, NULL); +} diff --git a/src/Native/System.Net.Security.Native/pal_gssapi.h b/src/Native/System.Net.Security.Native/pal_gssapi.h new file mode 100644 index 000000000000..6da751fe962c --- /dev/null +++ b/src/Native/System.Net.Security.Native/pal_gssapi.h @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma once +#include "pal_types.h" + +#ifdef GSSFW +#include +#else +#include +#include +#endif + + +enum GssStatus : uint32_t +{ + PAL_GSS_COMPLETE = 0, + PAL_GSS_CONTINUE_NEEDED = 1 +}; + +/* +Shims the gss_release_buffer method. +*/ +extern "C" uint32_t GssReleaseBuffer(uint32_t* minor_status, gss_buffer_t buffer); + +/* +Shims the gss_display_status method. +*/ +extern "C" uint32_t GssDisplayStatus(uint32_t* minorStatus, uint32_t statusValue, bool isGssMechCode, gss_buffer_t statusString); + +/* +Shims the gss_display_name method. +*/ +extern "C" uint32_t GssDisplayName(uint32_t* minorStatus, gss_name_t inputName, gss_buffer_t outputNameBuffer); + +/* +Shims the gss_import_name method. +*/ +extern "C" uint32_t GssImportName(uint32_t* minorStatus, char* inputName, bool isUser, gss_name_t* outputName); + +/* +Shims the gss_release_name method. +*/ +extern "C" uint32_t GssReleaseName(uint32_t* minorStatus, gss_name_t* inputName); + +/* +Shims the gss_acquire_cred method with SPNEGO oids. +*/ +extern "C" uint32_t GssAcquireCredSpNego(uint32_t* minorStatus, gss_name_t desiredName, bool isInitiate, gss_cred_id_t* outputCredHandle); + +/* +Shims the gss_release_cred method. +*/ +extern "C" uint32_t GssReleaseCred(uint32_t* minorStatus, gss_cred_id_t* credHandle); + +/* +Shims the gss_init_sec_context method with SPNEGO oids. +*/ +extern "C" uint32_t GssInitSecContext(uint32_t* minorStatus, gss_cred_id_t claimantCredHandle, gss_ctx_id_t* contextHandle, + bool isNtlm, gss_name_t targetName, uint32_t reqFlags, uint8_t* inputBytes, int32_t inputLength, gss_buffer_t outputToken, uint32_t* retFlags); + +/* +Shims the gss_delete_sec_context method. +*/ +extern "C" uint32_t GssDeleteSecContext(uint32_t* minorStatus, gss_ctx_id_t* contextHandle); + +/* +Shims the gss_wrap method. +*/ +extern "C" uint32_t GssWrap(uint32_t* minorStatus, gss_ctx_id_t contextHandle, bool isEncrypt, + uint8_t* inputBytes, int32_t offset, int32_t count, gss_buffer_t outputMessageBuffer); + +/* +Shims the gss_unwrap method. +*/ +extern "C" uint32_t GssUnwrap(uint32_t* minorStatus, gss_ctx_id_t contextHandle, + uint8_t* inputBytes, int32_t offset, int32_t count, gss_buffer_t outputMessageBuffer); + +/* +Shims the gss_inquire_context method. +*/ +extern "C" uint32_t GssInquireSourceName(uint32_t* minorStatus, gss_ctx_id_t contextHandle, gss_name_t* srcName); + + +/* +Shims the gss_acquire_cred_with_password method. +*/ +extern "C" uint32_t GssAcquireCredWithPassword(uint32_t* minorStatus, const gss_name_t desiredName, char* password, bool isInitiate, + gss_cred_id_t* outputCredHandle); diff --git a/src/System.Net.Security/src/Resources/Strings.resx b/src/System.Net.Security/src/Resources/Strings.resx index b1f411c38221..f44c99178ddd 100644 --- a/src/System.Net.Security/src/Resources/Strings.resx +++ b/src/System.Net.Security/src/Resources/Strings.resx @@ -1,4 +1,4 @@ - +