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
306 changes: 119 additions & 187 deletions src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -85,146 +85,6 @@ private static int GssUnwrap(
}
}

private static bool GssInitSecurityContext(

@filipnavarafilipnavaraJun 30, 2022

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

The GssInitSecurityContext and GssAcceptSecurityContext methods are folded into the caller. This made updating the handles substantially easier. I also removed the code that threw GssApiException only to catch it one method above in the stack and convert it to reported status code.

ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
bool isNtlm,
ChannelBinding? channelBinding,
SafeGssNameHandle? targetName,
Interop.NetSecurityNative.GssFlags inFlags,
ReadOnlySpan<byte> buffer,
out byte[]? outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
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.
bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
cbtAppData,
cbtAppDataSize,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static bool GssAcceptSecurityContext(
ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
ReadOnlySpan<byte> buffer,
out byte[] outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
Debug.Assert(credential != null);

bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
credential,
ref context,
buffer,
ref token,
out outFlags,
out isNtlmUsed);

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static string GssGetUser(
ref SafeGssContextHandle? context)
{
Expand DownExpand Up@@ -287,27 +147,70 @@ private static SecurityStatusPal EstablishSecurityContext(
context = new SafeDeleteNegoContext(credential, targetName!);
}

Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)context;
SafeGssContextHandle contextHandle = negoContext.GssContext;
try
{
Interop.NetSecurityNative.GssFlags inputFlags =
ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(inFlags, isServer: false);
uint outputFlags;
bool isNtlmUsed;
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssInitSecurityContext(
ref contextHandle,
credential.GssCredential,
isNtlmOnly,
channelBinding,
negoContext.TargetName,
inputFlags,
incomingBlob,
out resultBuffer,
out outputFlags,
out isNtlmUsed);

if (done)

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
cbtAppData,
cbtAppDataSize,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}

if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
if (negoContext.GssContext.IsInvalid)
{
context.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBuffer = Array.Empty<byte>();
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBuffer = token.ToByteArray();

if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -322,17 +225,9 @@ private static SecurityStatusPal EstablishSecurityContext(
Debug.Assert(resultBuffer != null, "Unexpected null buffer returned by GssApi");
outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: false);
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
{
negoContext.SetGssContext(contextHandle!);
}

SecurityStatusPalErrorCode errorCode = done ?
(negoContext.IsNtlmUsed && resultBuffer.Length > 0 ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded) :
SecurityStatusPalErrorCode errorCode = status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE ?
SecurityStatusPalErrorCode.OK :
SecurityStatusPalErrorCode.ContinueNeeded;
return new SecurityStatusPal(errorCode);
}
Expand All@@ -341,6 +236,22 @@ private static SecurityStatusPal EstablishSecurityContext(
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from InitSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// InitSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

internal static SecurityStatusPal InitializeSecurityContext(
Expand DownExpand Up@@ -388,33 +299,44 @@ internal static SecurityStatusPal AcceptSecurityContext(
securityContext ??= new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle!);

SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
SafeGssContextHandle contextHandle = negoContext.GssContext;
Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
try
{
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssAcceptSecurityContext(
ref contextHandle,
negoContext.AcceptorCredential,
incomingBlob,
out resultBlob,
out uint outputFlags,
out bool isNtlmUsed);

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
negoContext.AcceptorCredential,
ref contextHandle,
incomingBlob,
ref token,
out uint outputFlags,
out bool isNtlmUsed);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
negoContext.SetGssContext(contextHandle!);
if (negoContext.GssContext.IsInvalid)
{
contextHandle.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBlob = token.ToByteArray();

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");

contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: true);
resultBlobLength = resultBlob.Length;

SecurityStatusPalErrorCode errorCode;
if (done)
if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -423,7 +345,7 @@ internal static SecurityStatusPal AcceptSecurityContext(
}

negoContext.SetAuthenticationPackage(isNtlmUsed);
errorCode = (isNtlmUsed && resultBlob.Length > 0) ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded;
errorCode = SecurityStatusPalErrorCode.OK;
}
else
{
Expand All@@ -432,18 +354,28 @@ internal static SecurityStatusPal AcceptSecurityContext(

return new SecurityStatusPal(errorCode);
}
catch (Interop.NetSecurityNative.GssApiException gex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}
catch (Exception ex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
resultBlobLength = 0;
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from AcceptSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// AcceptSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

// https://www.gnu.org/software/gss/reference/gss.pdf (page 25)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
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
306 changes: 119 additions & 187 deletions src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -85,146 +85,6 @@ private static int GssUnwrap(
}
}

private static bool GssInitSecurityContext(

@filipnavarafilipnavaraJun 30, 2022

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

The GssInitSecurityContext and GssAcceptSecurityContext methods are folded into the caller. This made updating the handles substantially easier. I also removed the code that threw GssApiException only to catch it one method above in the stack and convert it to reported status code.

ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
bool isNtlm,
ChannelBinding? channelBinding,
SafeGssNameHandle? targetName,
Interop.NetSecurityNative.GssFlags inFlags,
ReadOnlySpan<byte> buffer,
out byte[]? outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
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.
bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
cbtAppData,
cbtAppDataSize,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static bool GssAcceptSecurityContext(
ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
ReadOnlySpan<byte> buffer,
out byte[] outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
Debug.Assert(credential != null);

bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
credential,
ref context,
buffer,
ref token,
out outFlags,
out isNtlmUsed);

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static string GssGetUser(
ref SafeGssContextHandle? context)
{
Expand DownExpand Up@@ -287,27 +147,70 @@ private static SecurityStatusPal EstablishSecurityContext(
context = new SafeDeleteNegoContext(credential, targetName!);
}

Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)context;
SafeGssContextHandle contextHandle = negoContext.GssContext;
try
{
Interop.NetSecurityNative.GssFlags inputFlags =
ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(inFlags, isServer: false);
uint outputFlags;
bool isNtlmUsed;
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssInitSecurityContext(
ref contextHandle,
credential.GssCredential,
isNtlmOnly,
channelBinding,
negoContext.TargetName,
inputFlags,
incomingBlob,
out resultBuffer,
out outputFlags,
out isNtlmUsed);

if (done)

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
cbtAppData,
cbtAppDataSize,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}

if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
if (negoContext.GssContext.IsInvalid)
{
context.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBuffer = Array.Empty<byte>();
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBuffer = token.ToByteArray();

if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -322,17 +225,9 @@ private static SecurityStatusPal EstablishSecurityContext(
Debug.Assert(resultBuffer != null, "Unexpected null buffer returned by GssApi");
outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: false);
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
{
negoContext.SetGssContext(contextHandle!);
}

SecurityStatusPalErrorCode errorCode = done ?
(negoContext.IsNtlmUsed && resultBuffer.Length > 0 ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded) :
SecurityStatusPalErrorCode errorCode = status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE ?
SecurityStatusPalErrorCode.OK :
SecurityStatusPalErrorCode.ContinueNeeded;
return new SecurityStatusPal(errorCode);
}
Expand All@@ -341,6 +236,22 @@ private static SecurityStatusPal EstablishSecurityContext(
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from InitSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// InitSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

internal static SecurityStatusPal InitializeSecurityContext(
Expand DownExpand Up@@ -388,33 +299,44 @@ internal static SecurityStatusPal AcceptSecurityContext(
securityContext ??= new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle!);

SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
SafeGssContextHandle contextHandle = negoContext.GssContext;
Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
try
{
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssAcceptSecurityContext(
ref contextHandle,
negoContext.AcceptorCredential,
incomingBlob,
out resultBlob,
out uint outputFlags,
out bool isNtlmUsed);

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
negoContext.AcceptorCredential,
ref contextHandle,
incomingBlob,
ref token,
out uint outputFlags,
out bool isNtlmUsed);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
negoContext.SetGssContext(contextHandle!);
if (negoContext.GssContext.IsInvalid)
{
contextHandle.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBlob = token.ToByteArray();

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");

contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: true);
resultBlobLength = resultBlob.Length;

SecurityStatusPalErrorCode errorCode;
if (done)
if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -423,7 +345,7 @@ internal static SecurityStatusPal AcceptSecurityContext(
}

negoContext.SetAuthenticationPackage(isNtlmUsed);
errorCode = (isNtlmUsed && resultBlob.Length > 0) ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded;
errorCode = SecurityStatusPalErrorCode.OK;
}
else
{
Expand All@@ -432,18 +354,28 @@ internal static SecurityStatusPal AcceptSecurityContext(

return new SecurityStatusPal(errorCode);
}
catch (Interop.NetSecurityNative.GssApiException gex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}
catch (Exception ex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
resultBlobLength = 0;
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from AcceptSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// AcceptSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

// https://www.gnu.org/software/gss/reference/gss.pdf (page 25)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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
306 changes: 119 additions & 187 deletions src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -85,146 +85,6 @@ private static int GssUnwrap(
}
}

private static bool GssInitSecurityContext(

@filipnavarafilipnavaraJun 30, 2022

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

The GssInitSecurityContext and GssAcceptSecurityContext methods are folded into the caller. This made updating the handles substantially easier. I also removed the code that threw GssApiException only to catch it one method above in the stack and convert it to reported status code.

ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
bool isNtlm,
ChannelBinding? channelBinding,
SafeGssNameHandle? targetName,
Interop.NetSecurityNative.GssFlags inFlags,
ReadOnlySpan<byte> buffer,
out byte[]? outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
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.
bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
cbtAppData,
cbtAppDataSize,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static bool GssAcceptSecurityContext(
ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
ReadOnlySpan<byte> buffer,
out byte[] outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
Debug.Assert(credential != null);

bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
credential,
ref context,
buffer,
ref token,
out outFlags,
out isNtlmUsed);

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static string GssGetUser(
ref SafeGssContextHandle? context)
{
Expand DownExpand Up@@ -287,27 +147,70 @@ private static SecurityStatusPal EstablishSecurityContext(
context = new SafeDeleteNegoContext(credential, targetName!);
}

Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)context;
SafeGssContextHandle contextHandle = negoContext.GssContext;
try
{
Interop.NetSecurityNative.GssFlags inputFlags =
ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(inFlags, isServer: false);
uint outputFlags;
bool isNtlmUsed;
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssInitSecurityContext(
ref contextHandle,
credential.GssCredential,
isNtlmOnly,
channelBinding,
negoContext.TargetName,
inputFlags,
incomingBlob,
out resultBuffer,
out outputFlags,
out isNtlmUsed);

if (done)

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
cbtAppData,
cbtAppDataSize,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}

if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
if (negoContext.GssContext.IsInvalid)
{
context.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBuffer = Array.Empty<byte>();
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBuffer = token.ToByteArray();

if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -322,17 +225,9 @@ private static SecurityStatusPal EstablishSecurityContext(
Debug.Assert(resultBuffer != null, "Unexpected null buffer returned by GssApi");
outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: false);
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
{
negoContext.SetGssContext(contextHandle!);
}

SecurityStatusPalErrorCode errorCode = done ?
(negoContext.IsNtlmUsed && resultBuffer.Length > 0 ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded) :
SecurityStatusPalErrorCode errorCode = status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE ?
SecurityStatusPalErrorCode.OK :
SecurityStatusPalErrorCode.ContinueNeeded;
return new SecurityStatusPal(errorCode);
}
Expand All@@ -341,6 +236,22 @@ private static SecurityStatusPal EstablishSecurityContext(
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from InitSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// InitSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

internal static SecurityStatusPal InitializeSecurityContext(
Expand DownExpand Up@@ -388,33 +299,44 @@ internal static SecurityStatusPal AcceptSecurityContext(
securityContext ??= new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle!);

SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
SafeGssContextHandle contextHandle = negoContext.GssContext;
Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
try
{
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssAcceptSecurityContext(
ref contextHandle,
negoContext.AcceptorCredential,
incomingBlob,
out resultBlob,
out uint outputFlags,
out bool isNtlmUsed);

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
negoContext.AcceptorCredential,
ref contextHandle,
incomingBlob,
ref token,
out uint outputFlags,
out bool isNtlmUsed);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
negoContext.SetGssContext(contextHandle!);
if (negoContext.GssContext.IsInvalid)
{
contextHandle.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBlob = token.ToByteArray();

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");

contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: true);
resultBlobLength = resultBlob.Length;

SecurityStatusPalErrorCode errorCode;
if (done)
if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -423,7 +345,7 @@ internal static SecurityStatusPal AcceptSecurityContext(
}

negoContext.SetAuthenticationPackage(isNtlmUsed);
errorCode = (isNtlmUsed && resultBlob.Length > 0) ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded;
errorCode = SecurityStatusPalErrorCode.OK;
}
else
{
Expand All@@ -432,18 +354,28 @@ internal static SecurityStatusPal AcceptSecurityContext(

return new SecurityStatusPal(errorCode);
}
catch (Interop.NetSecurityNative.GssApiException gex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}
catch (Exception ex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
resultBlobLength = 0;
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from AcceptSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// AcceptSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

// https://www.gnu.org/software/gss/reference/gss.pdf (page 25)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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
306 changes: 119 additions & 187 deletions src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -85,146 +85,6 @@ private static int GssUnwrap(
}
}

private static bool GssInitSecurityContext(

@filipnavarafilipnavaraJun 30, 2022

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

The GssInitSecurityContext and GssAcceptSecurityContext methods are folded into the caller. This made updating the handles substantially easier. I also removed the code that threw GssApiException only to catch it one method above in the stack and convert it to reported status code.

ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
bool isNtlm,
ChannelBinding? channelBinding,
SafeGssNameHandle? targetName,
Interop.NetSecurityNative.GssFlags inFlags,
ReadOnlySpan<byte> buffer,
out byte[]? outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
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.
bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
cbtAppData,
cbtAppDataSize,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static bool GssAcceptSecurityContext(
ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
ReadOnlySpan<byte> buffer,
out byte[] outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
Debug.Assert(credential != null);

bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
credential,
ref context,
buffer,
ref token,
out outFlags,
out isNtlmUsed);

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static string GssGetUser(
ref SafeGssContextHandle? context)
{
Expand DownExpand Up@@ -287,27 +147,70 @@ private static SecurityStatusPal EstablishSecurityContext(
context = new SafeDeleteNegoContext(credential, targetName!);
}

Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)context;
SafeGssContextHandle contextHandle = negoContext.GssContext;
try
{
Interop.NetSecurityNative.GssFlags inputFlags =
ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(inFlags, isServer: false);
uint outputFlags;
bool isNtlmUsed;
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssInitSecurityContext(
ref contextHandle,
credential.GssCredential,
isNtlmOnly,
channelBinding,
negoContext.TargetName,
inputFlags,
incomingBlob,
out resultBuffer,
out outputFlags,
out isNtlmUsed);

if (done)

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
cbtAppData,
cbtAppDataSize,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}

if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
if (negoContext.GssContext.IsInvalid)
{
context.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBuffer = Array.Empty<byte>();
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBuffer = token.ToByteArray();

if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -322,17 +225,9 @@ private static SecurityStatusPal EstablishSecurityContext(
Debug.Assert(resultBuffer != null, "Unexpected null buffer returned by GssApi");
outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: false);
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
{
negoContext.SetGssContext(contextHandle!);
}

SecurityStatusPalErrorCode errorCode = done ?
(negoContext.IsNtlmUsed && resultBuffer.Length > 0 ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded) :
SecurityStatusPalErrorCode errorCode = status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE ?
SecurityStatusPalErrorCode.OK :
SecurityStatusPalErrorCode.ContinueNeeded;
return new SecurityStatusPal(errorCode);
}
Expand All@@ -341,6 +236,22 @@ private static SecurityStatusPal EstablishSecurityContext(
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from InitSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// InitSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

internal static SecurityStatusPal InitializeSecurityContext(
Expand DownExpand Up@@ -388,33 +299,44 @@ internal static SecurityStatusPal AcceptSecurityContext(
securityContext ??= new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle!);

SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
SafeGssContextHandle contextHandle = negoContext.GssContext;
Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
try
{
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssAcceptSecurityContext(
ref contextHandle,
negoContext.AcceptorCredential,
incomingBlob,
out resultBlob,
out uint outputFlags,
out bool isNtlmUsed);

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
negoContext.AcceptorCredential,
ref contextHandle,
incomingBlob,
ref token,
out uint outputFlags,
out bool isNtlmUsed);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
negoContext.SetGssContext(contextHandle!);
if (negoContext.GssContext.IsInvalid)
{
contextHandle.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBlob = token.ToByteArray();

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");

contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: true);
resultBlobLength = resultBlob.Length;

SecurityStatusPalErrorCode errorCode;
if (done)
if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -423,7 +345,7 @@ internal static SecurityStatusPal AcceptSecurityContext(
}

negoContext.SetAuthenticationPackage(isNtlmUsed);
errorCode = (isNtlmUsed && resultBlob.Length > 0) ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded;
errorCode = SecurityStatusPalErrorCode.OK;
}
else
{
Expand All@@ -432,18 +354,28 @@ internal static SecurityStatusPal AcceptSecurityContext(

return new SecurityStatusPal(errorCode);
}
catch (Interop.NetSecurityNative.GssApiException gex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}
catch (Exception ex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
resultBlobLength = 0;
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from AcceptSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// AcceptSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

// https://www.gnu.org/software/gss/reference/gss.pdf (page 25)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
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
306 changes: 119 additions & 187 deletions src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -85,146 +85,6 @@ private static int GssUnwrap(
}
}

private static bool GssInitSecurityContext(

@filipnavarafilipnavaraJun 30, 2022

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

The GssInitSecurityContext and GssAcceptSecurityContext methods are folded into the caller. This made updating the handles substantially easier. I also removed the code that threw GssApiException only to catch it one method above in the stack and convert it to reported status code.

ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
bool isNtlm,
ChannelBinding? channelBinding,
SafeGssNameHandle? targetName,
Interop.NetSecurityNative.GssFlags inFlags,
ReadOnlySpan<byte> buffer,
out byte[]? outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
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.
bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
cbtAppData,
cbtAppDataSize,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static bool GssAcceptSecurityContext(
ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
ReadOnlySpan<byte> buffer,
out byte[] outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
Debug.Assert(credential != null);

bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
credential,
ref context,
buffer,
ref token,
out outFlags,
out isNtlmUsed);

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static string GssGetUser(
ref SafeGssContextHandle? context)
{
Expand DownExpand Up@@ -287,27 +147,70 @@ private static SecurityStatusPal EstablishSecurityContext(
context = new SafeDeleteNegoContext(credential, targetName!);
}

Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)context;
SafeGssContextHandle contextHandle = negoContext.GssContext;
try
{
Interop.NetSecurityNative.GssFlags inputFlags =
ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(inFlags, isServer: false);
uint outputFlags;
bool isNtlmUsed;
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssInitSecurityContext(
ref contextHandle,
credential.GssCredential,
isNtlmOnly,
channelBinding,
negoContext.TargetName,
inputFlags,
incomingBlob,
out resultBuffer,
out outputFlags,
out isNtlmUsed);

if (done)

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
cbtAppData,
cbtAppDataSize,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}

if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
if (negoContext.GssContext.IsInvalid)
{
context.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBuffer = Array.Empty<byte>();
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBuffer = token.ToByteArray();

if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -322,17 +225,9 @@ private static SecurityStatusPal EstablishSecurityContext(
Debug.Assert(resultBuffer != null, "Unexpected null buffer returned by GssApi");
outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: false);
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
{
negoContext.SetGssContext(contextHandle!);
}

SecurityStatusPalErrorCode errorCode = done ?
(negoContext.IsNtlmUsed && resultBuffer.Length > 0 ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded) :
SecurityStatusPalErrorCode errorCode = status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE ?
SecurityStatusPalErrorCode.OK :
SecurityStatusPalErrorCode.ContinueNeeded;
return new SecurityStatusPal(errorCode);
}
Expand All@@ -341,6 +236,22 @@ private static SecurityStatusPal EstablishSecurityContext(
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from InitSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// InitSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

internal static SecurityStatusPal InitializeSecurityContext(
Expand DownExpand Up@@ -388,33 +299,44 @@ internal static SecurityStatusPal AcceptSecurityContext(
securityContext ??= new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle!);

SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
SafeGssContextHandle contextHandle = negoContext.GssContext;
Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
try
{
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssAcceptSecurityContext(
ref contextHandle,
negoContext.AcceptorCredential,
incomingBlob,
out resultBlob,
out uint outputFlags,
out bool isNtlmUsed);

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
negoContext.AcceptorCredential,
ref contextHandle,
incomingBlob,
ref token,
out uint outputFlags,
out bool isNtlmUsed);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
negoContext.SetGssContext(contextHandle!);
if (negoContext.GssContext.IsInvalid)
{
contextHandle.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBlob = token.ToByteArray();

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");

contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: true);
resultBlobLength = resultBlob.Length;

SecurityStatusPalErrorCode errorCode;
if (done)
if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -423,7 +345,7 @@ internal static SecurityStatusPal AcceptSecurityContext(
}

negoContext.SetAuthenticationPackage(isNtlmUsed);
errorCode = (isNtlmUsed && resultBlob.Length > 0) ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded;
errorCode = SecurityStatusPalErrorCode.OK;
}
else
{
Expand All@@ -432,18 +354,28 @@ internal static SecurityStatusPal AcceptSecurityContext(

return new SecurityStatusPal(errorCode);
}
catch (Interop.NetSecurityNative.GssApiException gex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}
catch (Exception ex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
resultBlobLength = 0;
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from AcceptSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// AcceptSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

// https://www.gnu.org/software/gss/reference/gss.pdf (page 25)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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
306 changes: 119 additions & 187 deletions src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -85,146 +85,6 @@ private static int GssUnwrap(
}
}

private static bool GssInitSecurityContext(

@filipnavarafilipnavaraJun 30, 2022

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

The GssInitSecurityContext and GssAcceptSecurityContext methods are folded into the caller. This made updating the handles substantially easier. I also removed the code that threw GssApiException only to catch it one method above in the stack and convert it to reported status code.

ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
bool isNtlm,
ChannelBinding? channelBinding,
SafeGssNameHandle? targetName,
Interop.NetSecurityNative.GssFlags inFlags,
ReadOnlySpan<byte> buffer,
out byte[]? outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
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.
bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
cbtAppData,
cbtAppDataSize,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static bool GssAcceptSecurityContext(
ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
ReadOnlySpan<byte> buffer,
out byte[] outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
Debug.Assert(credential != null);

bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
credential,
ref context,
buffer,
ref token,
out outFlags,
out isNtlmUsed);

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static string GssGetUser(
ref SafeGssContextHandle? context)
{
Expand DownExpand Up@@ -287,27 +147,70 @@ private static SecurityStatusPal EstablishSecurityContext(
context = new SafeDeleteNegoContext(credential, targetName!);
}

Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)context;
SafeGssContextHandle contextHandle = negoContext.GssContext;
try
{
Interop.NetSecurityNative.GssFlags inputFlags =
ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(inFlags, isServer: false);
uint outputFlags;
bool isNtlmUsed;
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssInitSecurityContext(
ref contextHandle,
credential.GssCredential,
isNtlmOnly,
channelBinding,
negoContext.TargetName,
inputFlags,
incomingBlob,
out resultBuffer,
out outputFlags,
out isNtlmUsed);

if (done)

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
cbtAppData,
cbtAppDataSize,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}

if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
if (negoContext.GssContext.IsInvalid)
{
context.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBuffer = Array.Empty<byte>();
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBuffer = token.ToByteArray();

if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -322,17 +225,9 @@ private static SecurityStatusPal EstablishSecurityContext(
Debug.Assert(resultBuffer != null, "Unexpected null buffer returned by GssApi");
outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: false);
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
{
negoContext.SetGssContext(contextHandle!);
}

SecurityStatusPalErrorCode errorCode = done ?
(negoContext.IsNtlmUsed && resultBuffer.Length > 0 ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded) :
SecurityStatusPalErrorCode errorCode = status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE ?
SecurityStatusPalErrorCode.OK :
SecurityStatusPalErrorCode.ContinueNeeded;
return new SecurityStatusPal(errorCode);
}
Expand All@@ -341,6 +236,22 @@ private static SecurityStatusPal EstablishSecurityContext(
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from InitSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// InitSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

internal static SecurityStatusPal InitializeSecurityContext(
Expand DownExpand Up@@ -388,33 +299,44 @@ internal static SecurityStatusPal AcceptSecurityContext(
securityContext ??= new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle!);

SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
SafeGssContextHandle contextHandle = negoContext.GssContext;
Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
try
{
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssAcceptSecurityContext(
ref contextHandle,
negoContext.AcceptorCredential,
incomingBlob,
out resultBlob,
out uint outputFlags,
out bool isNtlmUsed);

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
negoContext.AcceptorCredential,
ref contextHandle,
incomingBlob,
ref token,
out uint outputFlags,
out bool isNtlmUsed);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
negoContext.SetGssContext(contextHandle!);
if (negoContext.GssContext.IsInvalid)
{
contextHandle.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBlob = token.ToByteArray();

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");

contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: true);
resultBlobLength = resultBlob.Length;

SecurityStatusPalErrorCode errorCode;
if (done)
if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -423,7 +345,7 @@ internal static SecurityStatusPal AcceptSecurityContext(
}

negoContext.SetAuthenticationPackage(isNtlmUsed);
errorCode = (isNtlmUsed && resultBlob.Length > 0) ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded;
errorCode = SecurityStatusPalErrorCode.OK;
}
else
{
Expand All@@ -432,18 +354,28 @@ internal static SecurityStatusPal AcceptSecurityContext(

return new SecurityStatusPal(errorCode);
}
catch (Interop.NetSecurityNative.GssApiException gex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}
catch (Exception ex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
resultBlobLength = 0;
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from AcceptSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// AcceptSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

// https://www.gnu.org/software/gss/reference/gss.pdf (page 25)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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
306 changes: 119 additions & 187 deletions src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -85,146 +85,6 @@ private static int GssUnwrap(
}
}

private static bool GssInitSecurityContext(

@filipnavarafilipnavaraJun 30, 2022

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

The GssInitSecurityContext and GssAcceptSecurityContext methods are folded into the caller. This made updating the handles substantially easier. I also removed the code that threw GssApiException only to catch it one method above in the stack and convert it to reported status code.

ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
bool isNtlm,
ChannelBinding? channelBinding,
SafeGssNameHandle? targetName,
Interop.NetSecurityNative.GssFlags inFlags,
ReadOnlySpan<byte> buffer,
out byte[]? outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
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.
bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
cbtAppData,
cbtAppDataSize,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static bool GssAcceptSecurityContext(
ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
ReadOnlySpan<byte> buffer,
out byte[] outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
Debug.Assert(credential != null);

bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
credential,
ref context,
buffer,
ref token,
out outFlags,
out isNtlmUsed);

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static string GssGetUser(
ref SafeGssContextHandle? context)
{
Expand DownExpand Up@@ -287,27 +147,70 @@ private static SecurityStatusPal EstablishSecurityContext(
context = new SafeDeleteNegoContext(credential, targetName!);
}

Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)context;
SafeGssContextHandle contextHandle = negoContext.GssContext;
try
{
Interop.NetSecurityNative.GssFlags inputFlags =
ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(inFlags, isServer: false);
uint outputFlags;
bool isNtlmUsed;
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssInitSecurityContext(
ref contextHandle,
credential.GssCredential,
isNtlmOnly,
channelBinding,
negoContext.TargetName,
inputFlags,
incomingBlob,
out resultBuffer,
out outputFlags,
out isNtlmUsed);

if (done)

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
cbtAppData,
cbtAppDataSize,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}

if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
if (negoContext.GssContext.IsInvalid)
{
context.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBuffer = Array.Empty<byte>();
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBuffer = token.ToByteArray();

if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -322,17 +225,9 @@ private static SecurityStatusPal EstablishSecurityContext(
Debug.Assert(resultBuffer != null, "Unexpected null buffer returned by GssApi");
outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: false);
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
{
negoContext.SetGssContext(contextHandle!);
}

SecurityStatusPalErrorCode errorCode = done ?
(negoContext.IsNtlmUsed && resultBuffer.Length > 0 ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded) :
SecurityStatusPalErrorCode errorCode = status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE ?
SecurityStatusPalErrorCode.OK :
SecurityStatusPalErrorCode.ContinueNeeded;
return new SecurityStatusPal(errorCode);
}
Expand All@@ -341,6 +236,22 @@ private static SecurityStatusPal EstablishSecurityContext(
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from InitSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// InitSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

internal static SecurityStatusPal InitializeSecurityContext(
Expand DownExpand Up@@ -388,33 +299,44 @@ internal static SecurityStatusPal AcceptSecurityContext(
securityContext ??= new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle!);

SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
SafeGssContextHandle contextHandle = negoContext.GssContext;
Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
try
{
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssAcceptSecurityContext(
ref contextHandle,
negoContext.AcceptorCredential,
incomingBlob,
out resultBlob,
out uint outputFlags,
out bool isNtlmUsed);

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
negoContext.AcceptorCredential,
ref contextHandle,
incomingBlob,
ref token,
out uint outputFlags,
out bool isNtlmUsed);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
negoContext.SetGssContext(contextHandle!);
if (negoContext.GssContext.IsInvalid)
{
contextHandle.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBlob = token.ToByteArray();

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");

contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: true);
resultBlobLength = resultBlob.Length;

SecurityStatusPalErrorCode errorCode;
if (done)
if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -423,7 +345,7 @@ internal static SecurityStatusPal AcceptSecurityContext(
}

negoContext.SetAuthenticationPackage(isNtlmUsed);
errorCode = (isNtlmUsed && resultBlob.Length > 0) ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded;
errorCode = SecurityStatusPalErrorCode.OK;
}
else
{
Expand All@@ -432,18 +354,28 @@ internal static SecurityStatusPal AcceptSecurityContext(

return new SecurityStatusPal(errorCode);
}
catch (Interop.NetSecurityNative.GssApiException gex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}
catch (Exception ex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
resultBlobLength = 0;
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from AcceptSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// AcceptSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

// https://www.gnu.org/software/gss/reference/gss.pdf (page 25)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
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
306 changes: 119 additions & 187 deletions src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -85,146 +85,6 @@ private static int GssUnwrap(
}
}

private static bool GssInitSecurityContext(

@filipnavarafilipnavaraJun 30, 2022

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

The GssInitSecurityContext and GssAcceptSecurityContext methods are folded into the caller. This made updating the handles substantially easier. I also removed the code that threw GssApiException only to catch it one method above in the stack and convert it to reported status code.

ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
bool isNtlm,
ChannelBinding? channelBinding,
SafeGssNameHandle? targetName,
Interop.NetSecurityNative.GssFlags inFlags,
ReadOnlySpan<byte> buffer,
out byte[]? outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
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.
bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
cbtAppData,
cbtAppDataSize,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential,
ref context,
isNtlm,
targetName,
(uint)inFlags,
buffer,
ref token,
out outFlags,
out isNtlmUsed);
}

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static bool GssAcceptSecurityContext(
ref SafeGssContextHandle? context,
SafeGssCredHandle credential,
ReadOnlySpan<byte> buffer,
out byte[] outputBuffer,
out uint outFlags,
out bool isNtlmUsed)
{
Debug.Assert(credential != null);

bool newContext = false;
if (context == null)
{
newContext = true;
context = new SafeGssContextHandle();
}

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

try
{
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
credential,
ref context,
buffer,
ref token,
out outFlags,
out isNtlmUsed);

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

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

return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
}

private static string GssGetUser(
ref SafeGssContextHandle? context)
{
Expand DownExpand Up@@ -287,27 +147,70 @@ private static SecurityStatusPal EstablishSecurityContext(
context = new SafeDeleteNegoContext(credential, targetName!);
}

Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)context;
SafeGssContextHandle contextHandle = negoContext.GssContext;
try
{
Interop.NetSecurityNative.GssFlags inputFlags =
ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(inFlags, isServer: false);
uint outputFlags;
bool isNtlmUsed;
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssInitSecurityContext(
ref contextHandle,
credential.GssCredential,
isNtlmOnly,
channelBinding,
negoContext.TargetName,
inputFlags,
incomingBlob,
out resultBuffer,
out outputFlags,
out isNtlmUsed);

if (done)

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
cbtAppData,
cbtAppDataSize,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}
else
{
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credential.GssCredential,
ref contextHandle,
isNtlmOnly,
negoContext.TargetName,
(uint)inputFlags,
incomingBlob,
ref token,
out outputFlags,
out isNtlmUsed);
}

if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
if (negoContext.GssContext.IsInvalid)
{
context.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBuffer = Array.Empty<byte>();
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBuffer = token.ToByteArray();

if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -322,17 +225,9 @@ private static SecurityStatusPal EstablishSecurityContext(
Debug.Assert(resultBuffer != null, "Unexpected null buffer returned by GssApi");
outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: false);
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
{
negoContext.SetGssContext(contextHandle!);
}

SecurityStatusPalErrorCode errorCode = done ?
(negoContext.IsNtlmUsed && resultBuffer.Length > 0 ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded) :
SecurityStatusPalErrorCode errorCode = status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE ?
SecurityStatusPalErrorCode.OK :
SecurityStatusPalErrorCode.ContinueNeeded;
return new SecurityStatusPal(errorCode);
}
Expand All@@ -341,6 +236,22 @@ private static SecurityStatusPal EstablishSecurityContext(
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from InitSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// InitSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

internal static SecurityStatusPal InitializeSecurityContext(
Expand DownExpand Up@@ -388,33 +299,44 @@ internal static SecurityStatusPal AcceptSecurityContext(
securityContext ??= new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle!);

SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
SafeGssContextHandle contextHandle = negoContext.GssContext;
Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
try
{
SafeGssContextHandle? contextHandle = negoContext.GssContext;
bool done = GssAcceptSecurityContext(
ref contextHandle,
negoContext.AcceptorCredential,
incomingBlob,
out resultBlob,
out uint outputFlags,
out bool isNtlmUsed);

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;
status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
negoContext.AcceptorCredential,
ref contextHandle,
incomingBlob,
ref token,
out uint outputFlags,
out bool isNtlmUsed);

// Save the inner context handle for further calls to NetSecurity
Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
if (null == negoContext.GssContext)
if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
(status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
{
negoContext.SetGssContext(contextHandle!);
if (negoContext.GssContext.IsInvalid)
{
contextHandle.Dispose();
}

Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}

resultBlob = token.ToByteArray();

Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");

contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
(Interop.NetSecurityNative.GssFlags)outputFlags, isServer: true);
resultBlobLength = resultBlob.Length;

SecurityStatusPalErrorCode errorCode;
if (done)
if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
{
if (NetEventSource.Log.IsEnabled())
{
Expand All@@ -423,7 +345,7 @@ internal static SecurityStatusPal AcceptSecurityContext(
}

negoContext.SetAuthenticationPackage(isNtlmUsed);
errorCode = (isNtlmUsed && resultBlob.Length > 0) ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded;
errorCode = SecurityStatusPalErrorCode.OK;
}
else
{
Expand All@@ -432,18 +354,28 @@ internal static SecurityStatusPal AcceptSecurityContext(

return new SecurityStatusPal(errorCode);
}
catch (Interop.NetSecurityNative.GssApiException gex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
resultBlobLength = 0;
return new SecurityStatusPal(GetErrorCode(gex), gex);
}
catch (Exception ex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
resultBlobLength = 0;
return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
}
finally
{
token.Dispose();

// Save the inner context handle for further calls to NetSecurity
//
// For the first call `negoContext.GssContext` is invalid and we expect the
// inital handle to be returned from AcceptSecContext. For any subsequent
// call the handle should stay the same or it can be destroyed by the native
// AcceptSecContext call.
Debug.Assert(
negoContext.GssContext == contextHandle ||
negoContext.GssContext.IsInvalid ||
contextHandle.IsInvalid);
negoContext.SetGssContext(contextHandle);
}
}

// https://www.gnu.org/software/gss/reference/gss.pdf (page 25)
Expand Down
Loading