< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Security.NegotiateInternal.NTAuthenticationNet8
Assembly: CoreWCF.Primitives
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Security/NegotiateInternal/NTAuthenticationNet8.cs
Line coverage
13%
Covered lines: 13
Uncovered lines: 85
Coverable lines: 98
Total lines: 224
Line coverage: 13.2%
Branch coverage
2%
Covered branches: 2
Total branches: 75
Branch coverage: 2.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%22100%
CreateNegotiateAuthentication(...)100%110%
.ctor()100%11100%
CreateNegotiateAuthenticationServerOptions()0%880%
SetChannelBinding(...)0%10100%
SetExtendedProtectionPolicy(...)0%10100%
Encrypt(...)0%880%
GetIdentity()0%440%
GetOutgoingBlob(...)0%440%
Dispose()0%220%
ToErrorCode(...)0%17170%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Security/NegotiateInternal/NTAuthenticationNet8.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System;
 5using System.Buffers;
 6using System.ComponentModel;
 7using System.IO;
 8using System.IO.Pipelines;
 9using System.Linq;
 10using System.Reflection;
 11using System.Security.Authentication.ExtendedProtection;
 12using System.Security.Principal;
 13
 14namespace CoreWCF.Security.NegotiateInternal
 15{
 16    internal class NTAuthenticationNet8 : INTAuthenticationFacade
 17    {
 18        // value should match the Windows sspicli NTE_FAIL value
 19        // defined in winerror.h
 20        private const int NTE_FAIL = unchecked((int)0x80090020);
 21
 22        private static readonly Type s_negotiateAuthenticationType;
 23        private static readonly Type s_negotiateAuthenticationStatusCodeType;
 24        private static readonly MethodInfo s_getOutgoingBlob;
 25        private static readonly Delegate s_getOutgoingBlobInvoker;
 26        private static readonly Type s_serverOptionsType;
 27
 28        static NTAuthenticationNet8()
 29        {
 130            var securityAssembly = typeof(System.Net.Security.NegotiateStream).Assembly;
 131            s_serverOptionsType = securityAssembly.GetType("System.Net.Security.NegotiateAuthenticationServerOptions", t
 32
 133            s_negotiateAuthenticationType = securityAssembly.GetType("System.Net.Security.NegotiateAuthentication", true
 34
 135            s_negotiateAuthenticationStatusCodeType = securityAssembly.GetType("System.Net.Security.NegotiateAuthenticat
 36
 137            s_getOutgoingBlob = s_negotiateAuthenticationType.GetMethods().Single(m =>
 2338                "GetOutgoingBlob".Equals(m.Name, StringComparison.Ordinal) &&
 2339                typeof(byte[]).Equals(m.ReturnType));
 40
 141            s_getOutgoingBlobInvoker = LambdaExpressionBuilder.BuildFor(
 142                s_negotiateAuthenticationType,
 143                s_getOutgoingBlob).Compile();
 144        }
 45
 46
 47        private static IDisposable CreateNegotiateAuthentication(object serverOptions)
 48        {
 049            object[] parameters = new object[] { serverOptions };
 050            return (IDisposable)Activator.CreateInstance(s_negotiateAuthenticationType, parameters);
 51        }
 52
 53        private IDisposable _negotiateAuthentication;
 54        private ChannelBinding _channelBinding;
 55        private ExtendedProtectionPolicy _protectionPolicy;
 56
 157        public NTAuthenticationNet8()
 58        {
 59            //_negotiateAuthentication = NewNegotiateAuthentication();
 160        }
 61
 62        private object CreateNegotiateAuthenticationServerOptions()
 63        {
 064            dynamic serverOptions = Activator.CreateInstance(s_serverOptionsType);
 065            if (_channelBinding != null) serverOptions.ChannelBinding = _channelBinding;
 066            if (_protectionPolicy != null) serverOptions.ExtendedProtectionPolicy = _protectionPolicy;
 67
 068            return serverOptions;
 69        }
 70
 071        private dynamic NegotiateAuthentication => _negotiateAuthentication ??= CreateNegotiateAuthentication(CreateNego
 72
 73        public void SetChannelBinding(ChannelBinding channelBinding)
 74        {
 075            if (channelBinding == null) return;
 076            if (_negotiateAuthentication != null) throw new InvalidOperationException("Channel binding must be set befor
 077            if (_channelBinding != null && channelBinding != null && _channelBinding != channelBinding) throw new Invali
 78
 079            _channelBinding = channelBinding;
 080        }
 81
 82        public void SetExtendedProtectionPolicy(ExtendedProtectionPolicy protectionPolicy)
 83        {
 084            if (protectionPolicy == null) return;
 085            if (_negotiateAuthentication != null) throw new InvalidOperationException("Extended Protection Policy must b
 086            if (_protectionPolicy != null && protectionPolicy != null && _protectionPolicy != protectionPolicy) throw ne
 87
 088            _protectionPolicy = protectionPolicy;
 089        }
 90
 91        // https://learn.microsoft.com/en-us/dotnet/api/system.net.security.negotiateauthentication.isauthenticated?view
 092        public bool IsCompleted => ((dynamic)NegotiateAuthentication).IsAuthenticated;
 93
 94        // https://learn.microsoft.com/en-us/dotnet/api/system.net.security.negotiateauthentication.package?view=net-8.0
 095        public string Protocol => ((dynamic)NegotiateAuthentication).Package;
 96
 097        public bool IsValidContext { get; private set; } = false;
 98
 99        public byte[] Encrypt(byte[] input)
 100        {
 101            // https://learn.microsoft.com/en-us/dotnet/api/system.net.security.negotiateauthentication.wrap?view=net-8.
 102            // System.Net.Security.NegotiateAuthenticationStatusCode Wrap(ReadOnlySpan<byte> input, System.Buffers.IBuff
 103            //
 104            // SECURITY: requestEncryption MUST be true. Encrypt() backs ISspiNegotiation.Encrypt which
 105            // SspiNegotiationTokenAuthenticator.IssueServiceToken uses to wrap the SecurityContextToken
 106            // proof key into the RequestedProofToken returned in the WS-Trust RSTR. Wrapping with
 107            // requestEncryption=false produces an integrity-only (MIC) token under platform GSS, which
 108            // would expose the symmetric proof key in cleartext to any passive network observer when
 109            // the binding is not protected by TLS. After the call we also assert isEncrypted is true
 110            // so that we fail closed if the negotiated package cannot provide confidentiality (rather
 111            // than silently leaking the key). This matches the behaviour of the legacy .NET Framework
 112            // WindowsSspiNegotiation.Encrypt path which called SSPI EncryptMessage with sealing.
 113            //
 114            // Create the memory stream with an initial capacity twice the size of the input, as encryption may increase
 115            // This is a heuristic and will be more than enough for typical cases, but it allows us to avoid resizing th
 116            // If we're wrong (e.g. the encryption overhead is larger than the input size), MemoryStream will grow if ne
 0117            var memoryStream = new MemoryStream(input.Length * 2);
 0118            PipeWriter pipeWriter = PipeWriter.Create(memoryStream);
 0119            var statusCode = (int)(((dynamic)_negotiateAuthentication).Wrap(input, (IBufferWriter<byte>)pipeWriter, true
 120            // Safe to call GetAwaiter().GetResult() as PipeWriter is on top of a MemoryStream which does all writes syn
 0121            pipeWriter.FlushAsync().GetAwaiter().GetResult();
 122
 0123            var errorCode = ToErrorCode(statusCode);
 0124            if (errorCode != NegotiateInternalSecurityStatusErrorCode.OK)
 125            {
 0126                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
 0127                    new SecurityNegotiationException(SR.Format(SR.SspiWrapFailed, errorCode)));
 128            }
 129
 0130            if (!isEncrypted)
 131            {
 0132                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
 0133                    new SecurityNegotiationException(SR.SspiWrapDidNotEncrypt));
 134            }
 135
 0136            var output = memoryStream.ToArray();
 0137            return output;
 138        }
 139
 140        public IIdentity GetIdentity()
 141        {
 142            // https://learn.microsoft.com/en-us/dotnet/api/system.net.security.negotiateauthentication.remoteidentity?v
 0143            return ((dynamic)NegotiateAuthentication).RemoteIdentity;
 144        }
 145
 146        public byte[] GetOutgoingBlob(byte[] incomingBlob, out NegotiateInternalSecurityStatusPal status)
 147        {
 148            // https://learn.microsoft.com/en-us/dotnet/api/system.net.security.negotiateauthentication.getoutgoingblob?
 149            // byte[]? GetOutgoingBlob(ReadOnlySpan<byte> incomingBlob, out System.Net.Security.NegotiateAuthenticationS
 0150            object statusCode = Activator.CreateInstance(s_negotiateAuthenticationStatusCodeType);
 151
 0152            object[] parameters = new object[] { NegotiateAuthentication, incomingBlob, statusCode };
 0153            var result = (byte[]) s_getOutgoingBlobInvoker.DynamicInvoke(parameters);
 0154            statusCode = parameters[2];
 155
 0156            var internalStatusCode = ToErrorCode((int)statusCode);
 157
 0158            IsValidContext = internalStatusCode is NegotiateInternalSecurityStatusErrorCode.OK
 0159                or NegotiateInternalSecurityStatusErrorCode.ContinueNeeded
 0160                or NegotiateInternalSecurityStatusErrorCode.CompleteNeeded;
 161
 0162            Exception error = null;
 163
 0164            if (!IsValidContext)
 165            {
 0166                error = new Win32Exception(NTE_FAIL, statusCode.ToString());
 167            }
 168
 0169            status = new NegotiateInternalSecurityStatusPal(internalStatusCode, error);
 170
 0171            return result;
 172        }
 173
 174        public void Dispose()
 175        {
 0176            _negotiateAuthentication?.Dispose();
 0177        }
 178
 179        /// <summary>
 180        /// Convert the NegotiateAuthenticationStatusCode int value into the (likely corresponding)
 181        /// NegotiateInternalSecurityStatusErrorCode value
 182        /// </summary>
 183        private static NegotiateInternalSecurityStatusErrorCode ToErrorCode(int inputValue)
 184        {
 185            // https://learn.microsoft.com/en-us/dotnet/api/system.net.security.negotiateauthenticationstatuscode?view=n
 0186            return inputValue switch
 0187            {
 0188                // Completed - Operation completed successfully
 0189                0 => NegotiateInternalSecurityStatusErrorCode.OK,
 0190                // ContinueNeeded
 0191                1 => NegotiateInternalSecurityStatusErrorCode.ContinueNeeded,
 0192                // GenericFailure
 0193                2 => NegotiateInternalSecurityStatusErrorCode.InternalError,
 0194                // BadBinding
 0195                3 => NegotiateInternalSecurityStatusErrorCode.BadBinding,
 0196                // Unsupported
 0197                4 => NegotiateInternalSecurityStatusErrorCode.Unsupported,
 0198                // MessageAltered
 0199                5 => NegotiateInternalSecurityStatusErrorCode.MessageAltered,
 0200                // ContextExpired
 0201                6 => NegotiateInternalSecurityStatusErrorCode.ContextExpired,
 0202                // CredentialsExpired (Closest match)
 0203                7 => NegotiateInternalSecurityStatusErrorCode.UnknownCredentials,
 0204                // InvalidCredentials (Closest match)
 0205                8 => NegotiateInternalSecurityStatusErrorCode.UnknownCredentials,
 0206                // InvalidToken
 0207                9 => NegotiateInternalSecurityStatusErrorCode.InvalidToken,
 0208                // UnknownCredentials
 0209                10 => NegotiateInternalSecurityStatusErrorCode.UnknownCredentials,
 0210                // QopNotSupported
 0211                11 => NegotiateInternalSecurityStatusErrorCode.QopNotSupported,
 0212                // OutOfSequence
 0213                12 => NegotiateInternalSecurityStatusErrorCode.OutOfSequence,
 0214                // SecurityQosFailed
 0215                13 => NegotiateInternalSecurityStatusErrorCode.SecurityQosFailed,
 0216                // TargetUnknown
 0217                14 => NegotiateInternalSecurityStatusErrorCode.TargetUnknown,
 0218                // ImpersonationValidationFailed (Closest match)
 0219                15 => NegotiateInternalSecurityStatusErrorCode.NoImpersonation,
 0220                _ => NegotiateInternalSecurityStatusErrorCode.NotSet,
 0221            };
 222        }
 223    }
 224}