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
1 change: 1 addition & 0 deletions src/Common/src/Interop/Unix/Interop.Libraries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ private static partial class Libraries
// Shims
internal const string SystemNative = "System.Native";
internal const string HttpNative = "System.Net.Http.Native";
internal const string NetSecurityNative = "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";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// 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;

internal static partial class Interop
{
internal static class GssApi
{
internal static bool EstablishSecurityContext(
Copy link
Member

Choose a reason for hiding this comment

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

I can't remember if we've had this conversation before, but...

This is a lot of code being added to the Interop.*.cs files. Typically we've kept these as close to 1:1 with the underlying operation being wrapped as possible, typiclaly just the P/Invoke signature or maybe a 1-2 line wrapper if necessary, and then all of this additional logic ends up living in the consuming code.

Is there a reason it's done this way instead?

Copy link
Member

Choose a reason for hiding this comment

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

(Note that at this point I'm not suggesting we rework all of this before it's merged, but rather it should be done as cleanup after merging.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is being tracked via #7031 (item no 1)

Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason it's done this way instead?

The only reason we started down this path with Interop.OpenSsl.cs was to allow sharing of the interop code across libraries (eg. System.Net.HttpClient and System.Net.Security) since the 1:1 functions are not very usable by themselves. So if we are sure there is no other consumer for GSSAPI library so we can move all the logic back to NegotiateStreamPal.Unix.cs (or still keep as separate source file but under System.Net.Security\src)

ref SafeGssContextHandle context,
SafeGssCredHandle credential,
bool isNtlm,
SafeGssNameHandle targetName,
Interop.NetSecurityNative.GssFlags inFlags,
byte[] buffer,
out byte[] outputBuffer,
out uint outFlags)
{
outputBuffer = null;
outFlags = 0;

// 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.
if (context == null)
{
context = new SafeGssContextHandle();
}

Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
Interop.NetSecurityNative.Status status;

try
{
Interop.NetSecurityNative.Status minorStatus;
status = NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
targetName,
(uint)inFlags,
buffer,
(buffer == null) ? 0 : buffer.Length,
ref token,
out outFlags);

if ((status != NetSecurityNative.Status.GSS_S_COMPLETE) && (status != NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
throw new NetSecurityNative.GssApiException(status, minorStatus);
}

outputBuffer = token.ToByteArray();
}
finally
{
token.Dispose();
}

return status == NetSecurityNative.Status.GSS_S_COMPLETE;
}

internal static byte[] Encrypt(
SafeGssContextHandle context,
bool encrypt,
byte[] buffer,
int offset,
int count)
{
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");

Interop.NetSecurityNative.GssBuffer encryptedBuffer = default(Interop.NetSecurityNative.GssBuffer);
try
{

Copy link
Member

Choose a reason for hiding this comment

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

Nit: unnecessary blank line

NetSecurityNative.Status minorStatus;
NetSecurityNative.Status status = NetSecurityNative.WrapBuffer(out minorStatus, context, encrypt, buffer, offset, count, ref encryptedBuffer);
if (status != NetSecurityNative.Status.GSS_S_COMPLETE)
{
throw new NetSecurityNative.GssApiException(status, minorStatus);
}

return encryptedBuffer.ToByteArray();
}
finally
{
encryptedBuffer.Dispose();
}
}

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");

Interop.NetSecurityNative.GssBuffer decryptedBuffer = default(Interop.NetSecurityNative.GssBuffer);
try
{
NetSecurityNative.Status minorStatus;
NetSecurityNative.Status status = NetSecurityNative.UnwrapBuffer(out minorStatus, context, buffer, offset, count, ref decryptedBuffer);
if (status != NetSecurityNative.Status.GSS_S_COMPLETE)
{
throw new NetSecurityNative.GssApiException(status, minorStatus);
}

return decryptedBuffer.Copy(buffer, offset);
}
finally
{
decryptedBuffer.Dispose();
}
}
}
}


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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;

internal static partial class Interop
{
internal static partial class NetSecurityNative
{
internal sealed class GssApiException : Exception
{
private readonly Status _minorStatus;

public Status MinorStatus
{
get { return _minorStatus;}
}

public GssApiException(string message) : base(message)
{
}

public GssApiException(Status majorStatus, Status minorStatus)
: base(GetGssApiDisplayStatus(majorStatus, minorStatus))
{
HResult = (int)majorStatus;
_minorStatus = minorStatus;
}

private static string GetGssApiDisplayStatus(Status majorStatus, Status minorStatus)
{
string majorError = GetGssApiDisplayStatus(majorStatus, isMinor: false);
string minorError = GetGssApiDisplayStatus(minorStatus, isMinor: true);

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"));
}

private static string GetGssApiDisplayStatus(Status status, bool isMinor)
{
GssBuffer displayBuffer = default(GssBuffer);

try
{
Interop.NetSecurityNative.Status minStat;
Interop.NetSecurityNative.Status displayCallStatus = isMinor ?
DisplayMinorStatus(out minStat, status, ref displayBuffer):
DisplayMajorStatus(out minStat, status, ref displayBuffer);
return (Status.GSS_S_COMPLETE != displayCallStatus) ? null : Marshal.PtrToStringAnsi(displayBuffer._data);
}
finally
{
displayBuffer.Dispose();
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// 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 Microsoft.Win32.SafeHandles;

internal static partial class Interop
{
internal static partial class NetSecurityNative
{
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct GssBuffer : IDisposable
{
internal UInt64 _length;
internal IntPtr _data;

internal int Copy(byte[] destination, int offset)
{
Debug.Assert(destination != null, "target destination cannot be null");
Debug.Assert((offset >= 0 && offset < destination.Length) || destination.Length == 0, "invalid offset " + offset);

if (_data == IntPtr.Zero || _length == 0)
{
return 0;
}

// Using Convert.ToInt32 to throw an exception in the unlikely event of too large value of _length
int sourceLength = Convert.ToInt32(_length);
Copy link
Member

Choose a reason for hiding this comment

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

If the intention here is to have it throw for a too-large value, a comment to that effect would be useful.

int destinationAvailable = destination.Length - offset; // amount of space in the given buffer
if (sourceLength > destinationAvailable)
{
throw new NetSecurityNative.GssApiException(SR.Format(SR.net_context_buffer_too_small, sourceLength, destinationAvailable));
}

Marshal.Copy(_data, destination, offset, sourceLength);
return sourceLength;
}

internal byte[] ToByteArray()
{
if (_data == IntPtr.Zero || _length == 0)
{
return Array.Empty<byte>();
}

int destinationLength = Convert.ToInt32(_length);
byte[] destination = new byte[destinationLength];
Marshal.Copy(_data, destination, 0, destinationLength);
return destination;
}

public void Dispose()
{
if (_data != IntPtr.Zero)
{
Interop.NetSecurityNative.ReleaseGssBuffer(_data, _length);
_data = IntPtr.Zero;
}

_length = 0;
}
}
}
}
Loading