Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// 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;

namespace System.Net.Security
{
internal sealed class SafeFreeNegoCredentials : SafeFreeCredentials
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What value is this type providing? It seems like it exists purely to wrap a SafeGssCredHandle... why not just have the caller use a SafeGssCredHandle?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bartonjs just told me that this exists purely to satisfy the PAL contract... is that true? If so, we can leave it as-is for now, but we should revisit that contract, which sounds flawed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to be wrong about that, but I feel that when I've had similar confusion on other things that was the answer.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.. the platform-agnostic code requires a SafeFreeCredentials object since on Windows it is same for SslStream and NegotiateStream. On Unix this is different. For fallback to NTLM logic, you will actually see more logic inside SafeFreeNegoCredentials.

{
private SafeGssCredHandle _credential;

public SafeGssCredHandle GssCredential
{
get { return _credential; }
}

public SafeFreeNegoCredentials(string username, string password, string domain) : base(IntPtr.Zero, true)
{
bool ignore = false;
_credential = SafeGssCredHandle.Create(username, password, domain);
_credential.DangerousAddRef(ref ignore);
}

public override bool IsInvalid
{
get { return (null == _credential); }
}

protected override bool ReleaseHandle()
{
_credential.DangerousRelease();
_credential = null;
return true;
}
}

internal sealed class SafeDeleteNegoContext : SafeDeleteContext
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question as above... why is this type needed? It seems to just be a container for two other safe handles. (And if it is necessary for some reason, does it need to be managing the lifetime of the handles it contains, as in the other one?)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a parent/child relationship between SafeDeleteContext and SafeFreeCredentials. You can see the same pattern in Windows code of SslStream/NegotiateStream and Unix version of SslStream

{
private SafeGssNameHandle _targetName;
private SafeGssContextHandle _context;

public SafeGssNameHandle TargetName
{
get { return _targetName; }
}

public SafeGssContextHandle GssContext
{
get { return _context; }
}

public SafeDeleteNegoContext(SafeFreeNegoCredentials credential, string targetName)
: base(credential)
{
try
{
_targetName = SafeGssNameHandle.CreatePrincipal(targetName);
}
catch
{
Debug.Assert((null != credential), "Null credential in SafeDeleteNegoContext");
Dispose();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shrutigarg see other comment at SafeDeleteSslContext

throw;
}
}

public void SetGssContext(SafeGssContextHandle context)
{
Debug.Assert(!context.IsInvalid, "Invalid context passed to SafeDeleteNegoContext");
_context = context;
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
if (null != _context)
{
_context.Dispose();
_context = null;
}

if (_targetName != null)
{
_targetName.Dispose();
_targetName = null;
}
}
base.Dispose(disposing);
}
}
}
91 changes: 60 additions & 31 deletions src/Common/src/Interop/Unix/libssl/SecuritySafeHandles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,19 @@ protected override bool ReleaseHandle()
// Implementation of handles dependable on FreeCredentialsHandle
//
#if DEBUG
internal sealed class SafeFreeCredentials : DebugSafeHandle
internal abstract class SafeFreeCredentials : DebugSafeHandle
{
#else
internal sealed class SafeFreeCredentials : SafeHandle
internal abstract class SafeFreeCredentials : SafeHandle
{
#endif
protected SafeFreeCredentials(IntPtr handle, bool ownsHandle) : base(handle, ownsHandle)
{
}
}

internal sealed class SafeFreeSslCredentials : SafeFreeCredentials
{
private SafeX509Handle _certHandle;
private SafeEvpPKeyHandle _certKeyHandle;
private SslProtocols _protocols = SslProtocols.None;
Expand All @@ -87,7 +94,7 @@ internal EncryptionPolicy Policy
get { return _policy; }
}

public SafeFreeCredentials(X509Certificate certificate, SslProtocols protocols, EncryptionPolicy policy)
public SafeFreeSslCredentials(X509Certificate certificate, SslProtocols protocols, EncryptionPolicy policy)
: base(IntPtr.Zero, true)
{
Debug.Assert(
Expand Down Expand Up @@ -207,14 +214,50 @@ protected override bool ReleaseHandle()
}

#if DEBUG
internal sealed class SafeDeleteContext : DebugSafeHandle
internal abstract class SafeDeleteContext : DebugSafeHandle
{
#else
internal sealed class SafeDeleteContext : SafeHandle
internal abstract class SafeDeleteContext : SafeHandle
{
#endif
private readonly SafeFreeCredentials _credential;
private readonly SafeSslHandle _sslContext;
private SafeFreeCredentials _credential;

protected SafeDeleteContext(SafeFreeCredentials credential)
: base(IntPtr.Zero, true)
{
Debug.Assert((null != credential), "Invalid credential passed to SafeDeleteContext");

// When a credential handle is first associated with the context we keep credential
// ref count bumped up to ensure ordered finalization. The credential properties
// are used in the SSL/NEGO data structures and should survive the lifetime of
// the SSL/NEGO context
bool ignore = false;
_credential = credential;
_credential.DangerousAddRef(ref ignore);
}

public override bool IsInvalid
{
get { return (null == _credential); }
}

protected override bool ReleaseHandle()
{
Debug.Assert((null != _credential), "Null credential in SafeDeleteContext");
_credential.DangerousRelease();
_credential = null;
return true;
}

public override string ToString()
{
return handle.ToString();
}
}

internal sealed class SafeDeleteSslContext : SafeDeleteContext
{
private SafeSslHandle _sslContext;

public SafeSslHandle SslContext
{
Expand All @@ -224,18 +267,10 @@ public SafeSslHandle SslContext
}
}

public SafeDeleteContext(SafeFreeCredentials credential, bool isServer, bool remoteCertRequired)
: base(IntPtr.Zero, true)
public SafeDeleteSslContext(SafeFreeSslCredentials credential, bool isServer, bool remoteCertRequired)
: base(credential)
{
Debug.Assert((null != credential) && !credential.IsInvalid, "Invalid credential used in SafeDeleteContext");

// When a credential handle is first associated with the context we keep credential
// ref count bumped up to ensure ordered finalization. The certificate handle and
// key handle are used in the SSL data structures and should survive the lifetime of
// the SSL context
bool gotCredRef = false;
_credential = credential;
_credential.DangerousAddRef(ref gotCredRef);
Debug.Assert((null != credential) && !credential.IsInvalid, "Invalid credential used in SafeDeleteSslContext");

try
{
Expand All @@ -249,11 +284,8 @@ public SafeDeleteContext(SafeFreeCredentials credential, bool isServer, bool rem
}
catch(Exception ex)
{
if (gotCredRef)
{
_credential.DangerousRelease();
}
Debug.Write("Exception Caught. - " + ex);
Dispose();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shrutigarg we need to (implement) and call base.Dispose which should also set _credential to null to prevent double-free

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling Dispose here is right, but in Dispose implementation below you're calling _sslContext.Dispose, and if the ctor throws in the call to AllocateSslContext, _sslContext will be null, and the call to Dispose will dereference null. You need to check in Dispose that _sslContext is not null before disposing it. Then you should set it back to null.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok .. will add check .. like in other dispose.

throw;
}
}
Expand All @@ -266,26 +298,23 @@ public override bool IsInvalid
}
}

protected override bool ReleaseHandle()
{
Debug.Assert((null != _credential) && !_credential.IsInvalid, "Invalid credential saved in SafeDeleteContext");
_credential.DangerousRelease();
return true;
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
_sslContext.Dispose();
if (null != _sslContext)
{
_sslContext.Dispose();
_sslContext = null;
}
}

base.Dispose(disposing);
}

public override string ToString()
{
return IsInvalid ? String.Empty : handle.ToString();
return handle.ToString();
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/System.Net.Security/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -432,4 +432,13 @@
<data name="net_context_buffer_too_small" xml:space="preserve">
<value>Insufficient buffer space. Required: {0} Actual: {1}.</value>
</data>
<data name="net_nego_channel_binding_not_supported" xml:space="preserve">
<value>No support for channel binding on operating systems other than Windows</value>
</data>
<data name="net_nego_ntlm_not_supported" xml:space="preserve">
<value>NTLM is not supported</value>
</data>
<data name="net_nego_server_not_supported" xml:space="preserve">
<value>Server implementation is not supported</value>
</data>
</root>
19 changes: 19 additions & 0 deletions src/System.Net.Security/src/System.Net.Security.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@

<!-- NegotiateStream -->
<Compile Include="System\Net\NegotiateStreamPal.Unix.cs" />
<Compile Include="System\Net\ContextFlagsAdapterPal.Unix.cs" />

<!-- Interop -->
<Compile Include="$(CommonPath)\Interop\Unix\Interop.Libraries.cs">
Expand Down Expand Up @@ -306,6 +307,24 @@
<Compile Include="$(CommonPath)\Interop\Unix\System.Net.Security.Native\Interop.Initialization.cs">
<Link>Common\Interop\Unix\System.Net.Security.Native\Interop.Initialization.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Net.Security.Native\Interop.GssApi.cs">
<Link>Common\Interop\Unix\System.Net.Security.Native\Interop.GssApi.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Net.Security.Native\Interop.GssApiException.cs">
<Link>Common\Interop\Unix\System.Net.Security.Native\Interop.GssApiException.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Net.Security.Native\Interop.GssBuffer.cs">
<Link>Common\Interop\Unix\System.Net.Security.Native\Interop.GssBuffer.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Net.Security.Native\SecuritySafeHandles.cs">
<Link>Common\Interop\Unix\System.Net.Security.Native\SecuritySafeHandles.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\GssSafeHandles.cs">
<Link>Common\Microsoft\Win32\SafeHandles\GssSafeHandles.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.cs">
<Link>Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeX509Handles.Unix.cs">
<Link>Common\Microsoft\Win32\SafeHandles\SafeX509Handles.Unix.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ internal static X509Certificate2 GetRemoteCertificate(SafeDeleteContext security
remoteCertificateStore = new X509Certificate2Collection();

using (SafeSharedX509StackHandle chainStack =
Interop.OpenSsl.GetPeerCertificateChain(securityContext.SslContext))
Interop.OpenSsl.GetPeerCertificateChain(((SafeDeleteSslContext)securityContext).SslContext))
{
if (!chainStack.IsInvalid)
{
Expand Down Expand Up @@ -163,7 +163,7 @@ internal static X509Certificate2 GetRemoteCertificate(SafeDeleteContext security
//
internal static string[] GetRequestCertificateAuthorities(SafeDeleteContext securityContext)
{
using (SafeSharedX509NameStackHandle names = Interop.Ssl.SslGetClientCAList(securityContext.SslContext))
using (SafeSharedX509NameStackHandle names = Interop.Ssl.SslGetClientCAList(((SafeDeleteSslContext)securityContext).SslContext))
{
if (names.IsInvalid)
{
Expand Down Expand Up @@ -257,7 +257,7 @@ private static int QueryContextRemoteCertificate(SafeDeleteContext securityConte
remoteCertContext = null;
try
{
SafeX509Handle remoteCertificate = Interop.OpenSsl.GetPeerCertificate(securityContext.SslContext);
SafeX509Handle remoteCertificate = Interop.OpenSsl.GetPeerCertificate(((SafeDeleteSslContext)securityContext).SslContext);
// Note that cert ownership is transferred to SafeFreeCertContext
remoteCertContext = new SafeFreeCertContext(remoteCertificate);
return 0;
Expand Down
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;

namespace System.Net
{
internal static class ContextFlagsAdapterPal
{
private struct ContextFlagMapping
{
public readonly Interop.NetSecurityNative.GssFlags GssFlags;
public readonly ContextFlagsPal ContextFlag;

public ContextFlagMapping(Interop.NetSecurityNative.GssFlags gssFlag, ContextFlagsPal contextFlag)
{
GssFlags = gssFlag;
ContextFlag = contextFlag;
}
}

private static readonly ContextFlagMapping[] s_contextFlagMapping = new[]
{
// GSS_C_INTEG_FLAG is set if either AcceptIntegrity (used by server) or InitIntegrity (used by client) is set
new ContextFlagMapping(Interop.NetSecurityNative.GssFlags.GSS_C_INTEG_FLAG, ContextFlagsPal.AcceptIntegrity | ContextFlagsPal.InitIntegrity),
new ContextFlagMapping(Interop.NetSecurityNative.GssFlags.GSS_C_CONF_FLAG, ContextFlagsPal.Confidentiality),
new ContextFlagMapping(Interop.NetSecurityNative.GssFlags.GSS_C_IDENTIFY_FLAG, ContextFlagsPal.InitIdentify),
new ContextFlagMapping(Interop.NetSecurityNative.GssFlags.GSS_C_MUTUAL_FLAG, ContextFlagsPal.MutualAuth),
new ContextFlagMapping(Interop.NetSecurityNative.GssFlags.GSS_C_REPLAY_FLAG, ContextFlagsPal.ReplayDetect),
new ContextFlagMapping(Interop.NetSecurityNative.GssFlags.GSS_C_SEQUENCE_FLAG, ContextFlagsPal.SequenceDetect)
};


internal static ContextFlagsPal GetContextFlagsPalFromInterop(Interop.NetSecurityNative.GssFlags gssFlags)
{
ContextFlagsPal flags = ContextFlagsPal.Zero;
foreach (ContextFlagMapping mapping in s_contextFlagMapping)
{
if ((gssFlags & mapping.GssFlags) != 0)
{
flags |= mapping.ContextFlag;
}
}

return flags;
}

internal static Interop.NetSecurityNative.GssFlags GetInteropFromContextFlagsPal(ContextFlagsPal flags)
{
Interop.NetSecurityNative.GssFlags gssFlags = (Interop.NetSecurityNative.GssFlags)0;
foreach (ContextFlagMapping mapping in s_contextFlagMapping)
{
if ((flags & mapping.ContextFlag) != 0)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to map in GSS_C_INTEG_FLAG for either AcceptIntegrity or InitIntegrity (not requiring them both to be set). If you need them both to be set, this should be == mapping.ContextFlag instead of != 0. If "either" is what you want, it might be worthy of a comment. (And if "both" is what you want, that might also warrant a comment :))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"either" is what is needed since Accept is used by server and Init by client. @shrutigarg please add a comment to that effect

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added comment

{
gssFlags |= mapping.GssFlags;
}
}

return gssFlags;
}
}
}
Loading