private static SecurityStatusPalErrorCode GetErrorCode(Interop.NetSecurityNative.GssApiException exception) { switch (exception.MajorStatus) { case Interop.NetSecurityNative.Status.GSS_S_NO_CRED: return(SecurityStatusPalErrorCode.UnknownCredentials); case Interop.NetSecurityNative.Status.GSS_S_BAD_BINDINGS: return(SecurityStatusPalErrorCode.BadBinding); case Interop.NetSecurityNative.Status.GSS_S_CREDENTIALS_EXPIRED: return(SecurityStatusPalErrorCode.CertExpired); case Interop.NetSecurityNative.Status.GSS_S_DEFECTIVE_TOKEN: return(SecurityStatusPalErrorCode.InvalidToken); case Interop.NetSecurityNative.Status.GSS_S_DEFECTIVE_CREDENTIAL: return(SecurityStatusPalErrorCode.IncompleteCredentials); case Interop.NetSecurityNative.Status.GSS_S_BAD_SIG: return(SecurityStatusPalErrorCode.MessageAltered); case Interop.NetSecurityNative.Status.GSS_S_BAD_MECH: return(SecurityStatusPalErrorCode.Unsupported); case Interop.NetSecurityNative.Status.GSS_S_NO_CONTEXT: default: return(SecurityStatusPalErrorCode.InternalError); } }
private static SecurityStatusPal EstablishSecurityContext( SafeFreeNegoCredentials credential, ref SafeDeleteContext?context, ChannelBinding?channelBinding, string?targetName, ContextFlagsPal inFlags, ReadOnlySpan <byte> incomingBlob, out byte[]?resultBuffer, ref ContextFlagsPal outFlags) { bool isNtlmOnly = credential.IsNtlmOnly; resultBuffer = null; if (context == null) { if (NetEventSource.Log.IsEnabled()) { string protocol = isNtlmOnly ? "NTLM" : "SPNEGO"; NetEventSource.Info(context, $"requested protocol = {protocol}, target = {targetName}"); } 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; 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()) { string protocol = isNtlmOnly ? "NTLM" : isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos"; NetEventSource.Info(context, $"actual protocol = {protocol}"); } // Populate protocol used for authentication negoContext.SetAuthenticationPackage(isNtlmUsed); } Debug.Assert(resultBuffer != null, "Unexpected null buffer returned by GssApi"); outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop( (Interop.NetSecurityNative.GssFlags)outputFlags, isServer: false); SecurityStatusPalErrorCode errorCode = status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.ContinueNeeded; return(new SecurityStatusPal(errorCode)); } catch (Exception ex) { 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 AcceptSecurityContext( SafeFreeCredentials?credentialsHandle, ref SafeDeleteContext?securityContext, ContextFlagsPal requestedContextFlags, ReadOnlySpan <byte> incomingBlob, ChannelBinding?channelBinding, ref byte[] resultBlob, out int resultBlobLength, ref ContextFlagsPal contextFlags) { securityContext ??= new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle !); SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext; SafeGssContextHandle contextHandle = negoContext.GssContext; Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer); try { 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); if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) && (status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED)) { 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 (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE) { if (NetEventSource.Log.IsEnabled()) { string protocol = isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos"; NetEventSource.Info(securityContext, $"AcceptSecurityContext: actual protocol = {protocol}"); } negoContext.SetAuthenticationPackage(isNtlmUsed); errorCode = SecurityStatusPalErrorCode.OK; } else { errorCode = SecurityStatusPalErrorCode.ContinueNeeded; } return(new SecurityStatusPal(errorCode)); } 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); } }
public void WriteHandshakeFrame(Interop.NetSecurityNative.GssApiException e) { WriteFrameHeader(ErrorBuffer.Length, isError: true); _innerStream.Write(ErrorBuffer, 0, ErrorBuffer.Length); }