Skip to content

Commit

Permalink
Add support for specifying Kerberos package on Linux/macOS (in additi…
Browse files Browse the repository at this point in the history
…on to NTLM and Negotiate)
  • Loading branch information
filipnavara authored Jul 12, 2022
1 parent e928da5 commit c6619e1
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 58 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

internal static partial class Interop
{
internal static partial class NetSecurityNative
{
internal enum PackageType : uint
{
Negotiate = 0,
NTLM = 1,
Kerberos = 2,
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ internal static partial Status InitiateCredSpNego(
[LibraryImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_InitiateCredWithPassword", StringMarshalling = StringMarshalling.Utf8)]
internal static partial Status InitiateCredWithPassword(
out Status minorStatus,
[MarshalAs(UnmanagedType.Bool)] bool isNtlm,
PackageType packageType,
SafeGssNameHandle desiredName,
string password,
int passwordLen,
Expand All @@ -77,7 +77,7 @@ private static partial Status InitSecContext(
out Status minorStatus,
SafeGssCredHandle initiatorCredHandle,
ref SafeGssContextHandle contextHandle,
[MarshalAs(UnmanagedType.Bool)] bool isNtlmOnly,
PackageType packageType,
SafeGssNameHandle? targetName,
uint reqFlags,
ref byte inputBytes,
Expand All @@ -91,7 +91,7 @@ private static partial Status InitSecContext(
out Status minorStatus,
SafeGssCredHandle initiatorCredHandle,
ref SafeGssContextHandle contextHandle,
[MarshalAs(UnmanagedType.Bool)] bool isNtlmOnly,
PackageType packageType,
IntPtr cbt,
int cbtSize,
SafeGssNameHandle? targetName,
Expand All @@ -106,7 +106,7 @@ internal static Status InitSecContext(
out Status minorStatus,
SafeGssCredHandle initiatorCredHandle,
ref SafeGssContextHandle contextHandle,
bool isNtlmOnly,
PackageType packageType,
SafeGssNameHandle? targetName,
uint reqFlags,
ReadOnlySpan<byte> inputBytes,
Expand All @@ -118,7 +118,7 @@ internal static Status InitSecContext(
out minorStatus,
initiatorCredHandle,
ref contextHandle,
isNtlmOnly,
packageType,
targetName,
reqFlags,
ref MemoryMarshal.GetReference(inputBytes),
Expand All @@ -132,7 +132,7 @@ internal static Status InitSecContext(
out Status minorStatus,
SafeGssCredHandle initiatorCredHandle,
ref SafeGssContextHandle contextHandle,
bool isNtlmOnly,
PackageType packageType,
IntPtr cbt,
int cbtSize,
SafeGssNameHandle? targetName,
Expand All @@ -146,7 +146,7 @@ internal static Status InitSecContext(
out minorStatus,
initiatorCredHandle,
ref contextHandle,
isNtlmOnly,
packageType,
cbt,
cbtSize,
targetName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ public static SafeGssCredHandle CreateAcceptor()
/// returns the handle for the given credentials.
/// The method returns an invalid handle if the username is null or empty.
/// </summary>
public static SafeGssCredHandle Create(string username, string password, bool isNtlmOnly)
public static SafeGssCredHandle Create(string username, string password, Interop.NetSecurityNative.PackageType packageType)
{
if (isNtlmOnly && !s_IsNtlmInstalled.Value)
if (packageType == Interop.NetSecurityNative.PackageType.NTLM && !s_IsNtlmInstalled.Value)
{
throw new Interop.NetSecurityNative.GssApiException(
Interop.NetSecurityNative.Status.GSS_S_BAD_MECH,
Expand All @@ -117,7 +117,7 @@ public static SafeGssCredHandle Create(string username, string password, bool is
}
else
{
status = Interop.NetSecurityNative.InitiateCredWithPassword(out minorStatus, isNtlmOnly, userHandle, password, Encoding.UTF8.GetByteCount(password), out retHandle);
status = Interop.NetSecurityNative.InitiateCredWithPassword(out minorStatus, packageType, userHandle, password, Encoding.UTF8.GetByteCount(password), out retHandle);
}

if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,19 @@ private static SecurityStatusPal EstablishSecurityContext(
out byte[]? resultBuffer,
ref ContextFlagsPal outFlags)
{
bool isNtlmOnly = credential.IsNtlmOnly;
Interop.NetSecurityNative.PackageType packageType = credential.PackageType;

resultBuffer = null;

if (context == null)
{
if (NetEventSource.Log.IsEnabled())
{
string protocol = isNtlmOnly ? "NTLM" : "SPNEGO";
string protocol = packageType switch {
Interop.NetSecurityNative.PackageType.NTLM => "NTLM",
Interop.NetSecurityNative.PackageType.Kerberos => "Kerberos",
_ => "SPNEGO"
};
NetEventSource.Info(context, $"requested protocol = {protocol}, target = {targetName}");
}

Expand Down Expand Up @@ -172,7 +176,7 @@ private static SecurityStatusPal EstablishSecurityContext(
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
packageType,
cbtAppData,
cbtAppDataSize,
negoContext.TargetName,
Expand All @@ -187,7 +191,7 @@ private static SecurityStatusPal EstablishSecurityContext(
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
packageType,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
Expand Down Expand Up @@ -216,7 +220,11 @@ private static SecurityStatusPal EstablishSecurityContext(
{
if (NetEventSource.Log.IsEnabled())
{
string protocol = isNtlmOnly ? "NTLM" : isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos";
string protocol = packageType switch {
Interop.NetSecurityNative.PackageType.NTLM => "NTLM",
Interop.NetSecurityNative.PackageType.Kerberos => "Kerberos",
_ => isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos"
};
NetEventSource.Info(context, $"actual protocol = {protocol}");
}

Expand Down Expand Up @@ -441,24 +449,36 @@ internal static SafeFreeCredentials AcquireCredentialsHandle(string package, boo
{
bool isEmptyCredential = string.IsNullOrWhiteSpace(credential.UserName) ||
string.IsNullOrWhiteSpace(credential.Password);
bool ntlmOnly = string.Equals(package, NegotiationInfoClass.NTLM, StringComparison.OrdinalIgnoreCase);
if (ntlmOnly && isEmptyCredential && !isServer)
Interop.NetSecurityNative.PackageType packageType;

if (string.Equals(package, NegotiationInfoClass.Negotiate, StringComparison.OrdinalIgnoreCase))
{
// NTLM authentication is not possible with default credentials which are no-op
throw new PlatformNotSupportedException(SR.net_ntlm_not_possible_default_cred);
packageType = Interop.NetSecurityNative.PackageType.Negotiate;
}

if (!ntlmOnly && !string.Equals(package, NegotiationInfoClass.Negotiate))
else if (string.Equals(package, NegotiationInfoClass.NTLM, StringComparison.OrdinalIgnoreCase))
{
packageType = Interop.NetSecurityNative.PackageType.NTLM;
if (isEmptyCredential && !isServer)
{
// NTLM authentication is not possible with default credentials which are no-op
throw new PlatformNotSupportedException(SR.net_ntlm_not_possible_default_cred);
}
}
else if (string.Equals(package, NegotiationInfoClass.Kerberos, StringComparison.OrdinalIgnoreCase))
{
packageType = Interop.NetSecurityNative.PackageType.Kerberos;
}
else
{
// Native shim currently supports only NTLM and Negotiate
// Native shim currently supports only NTLM, Negotiate and Kerberos
throw new PlatformNotSupportedException(SR.net_securitypackagesupport);
}

try
{
return isEmptyCredential ?
new SafeFreeNegoCredentials(ntlmOnly, string.Empty, string.Empty, string.Empty) :
new SafeFreeNegoCredentials(ntlmOnly, credential.UserName, credential.Password, credential.Domain);
new SafeFreeNegoCredentials(packageType, string.Empty, string.Empty, string.Empty) :
new SafeFreeNegoCredentials(packageType, credential.UserName, credential.Password, credential.Domain);
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace System.Net.Security
internal sealed class SafeFreeNegoCredentials : SafeFreeCredentials
{
private SafeGssCredHandle _credential;
private readonly bool _isNtlmOnly;
private readonly Interop.NetSecurityNative.PackageType _packageType;
private readonly string _userName;
private readonly bool _isDefault;

Expand All @@ -21,10 +21,10 @@ public SafeGssCredHandle GssCredential
get { return _credential; }
}

// Property represents if Ntlm Protocol is specfied or not.
public bool IsNtlmOnly
// Property represents which protocol is specfied (Negotiate, Ntlm or Kerberos).
public Interop.NetSecurityNative.PackageType PackageType
{
get { return _isNtlmOnly; }
get { return _packageType; }
}

public string UserName
Expand All @@ -37,7 +37,7 @@ public bool IsDefault
get { return _isDefault; }
}

public SafeFreeNegoCredentials(bool isNtlmOnly, string username, string password, string domain)
public SafeFreeNegoCredentials(Interop.NetSecurityNative.PackageType packageType, string username, string password, string domain)
: base(IntPtr.Zero, true)
{
Debug.Assert(username != null && password != null, "Username and Password can not be null");
Expand Down Expand Up @@ -66,10 +66,10 @@ public SafeFreeNegoCredentials(bool isNtlmOnly, string username, string password
}

bool ignore = false;
_isNtlmOnly = isNtlmOnly;
_packageType = packageType;
_userName = username;
_isDefault = string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password);
_credential = SafeGssCredHandle.Create(username, password, isNtlmOnly);
_credential = SafeGssCredHandle.Create(username, password, packageType);
_credential.DangerousAddRef(ref ignore);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@
Link="Common\Interop\Unix\Interop.Errors.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.GssFlags.cs"
Link="Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.GssFlags.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.PackageType.cs"
Link="Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.PackageType.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.Status.cs"
Link="Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.Status.cs" />
<Compile Include="$(CommonPath)System\Net\Security\Unix\SafeDeleteContext.cs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@
Link="Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.GssFlags.cs"
Link="Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.GssFlags.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.PackageType.cs"
Link="Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.PackageType.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.Status.cs"
Link="Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.Status.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.IsNtlmInstalled.cs"
Expand Down
Loading

0 comments on commit c6619e1

Please sign in to comment.