< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Channels.SslStreamSecurityUpgradeAcceptor
Assembly: CoreWCF.NetFramingBase
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.NetFramingBase/src/CoreWCF/Channels/SslStreamSecurityUpgradeProvider.cs
Line coverage
71%
Covered lines: 33
Uncovered lines: 13
Coverable lines: 46
Total lines: 323
Line coverage: 71.7%
Branch coverage
66%
Covered branches: 8
Total branches: 12
Branch coverage: 66.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
OnAcceptUpgradeAsync()50%2253.33%
ValidateRemoteCertificate(...)66.66%6680%
GetRemoteSecurity()75%4480%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.NetFramingBase/src/CoreWCF/Channels/SslStreamSecurityUpgradeProvider.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.Collections.ObjectModel;
 6using System.Diagnostics;
 7using System.IO;
 8using System.Net.Security;
 9using System.Security.Authentication;
 10using System.Security.Authentication.ExtendedProtection;
 11using System.Security.Cryptography.X509Certificates;
 12using System.Threading;
 13using System.Threading.Tasks;
 14using CoreWCF.Description;
 15using CoreWCF.IdentityModel.Policy;
 16using CoreWCF.IdentityModel.Selectors;
 17using CoreWCF.IdentityModel.Tokens;
 18using CoreWCF.Runtime;
 19using CoreWCF.Security;
 20using CoreWCF.Security.Tokens;
 21
 22namespace CoreWCF.Channels
 23{
 24    internal class SslStreamSecurityUpgradeProvider : StreamSecurityUpgradeProvider, IStreamUpgradeChannelBindingProvide
 25    {
 26        private SecurityTokenAuthenticator _clientCertificateAuthenticator;
 27        private SecurityTokenProvider _serverTokenProvider;
 28        private EndpointIdentity _identity;
 29        private bool _enableChannelBinding;
 30        // This is the equivalent of X509ClientCertificateAuthentication.DefaultCertificateValidator
 31        private static X509CertificateValidator s_defaultX509CertificateValidator = X509CertificateValidator.CreateChain
 32
 33        private SslStreamSecurityUpgradeProvider(IDefaultCommunicationTimeouts timeouts, SecurityTokenProvider serverTok
 34            : base(timeouts)
 35        {
 36            _serverTokenProvider = serverTokenProvider;
 37            RequireClientCertificate = requireClientCertificate;
 38            _clientCertificateAuthenticator = clientCertificateAuthenticator;
 39            IdentityVerifier = identityVerifier;
 40            Scheme = scheme;
 41            SslProtocols = sslProtocols;
 42            ClientSecurityTokenManager = null; // Used for client but there's public api which need this and the compile
 43        }
 44
 45        public static SslStreamSecurityUpgradeProvider CreateServerProvider(
 46            SslStreamSecurityBindingElement bindingElement, BindingContext context)
 47        {
 48            SecurityCredentialsManager credentialProvider =
 49                context.BindingParameters.Find<SecurityCredentialsManager>();
 50
 51            if (credentialProvider == null)
 52            {
 53                credentialProvider = new ServiceCredentials();
 54            }
 55
 56            Uri listenUri = TransportSecurityHelpers.GetListenUri(context.ListenUriBaseAddress, context.ListenUriRelativ
 57            SecurityTokenManager tokenManager = credentialProvider.CreateSecurityTokenManager();
 58
 59            RecipientServiceModelSecurityTokenRequirement serverCertRequirement = new RecipientServiceModelSecurityToken
 60            {
 61                TokenType = SecurityTokenTypes.X509Certificate,
 62                RequireCryptographicToken = true,
 63                KeyUsage = SecurityKeyUsage.Exchange,
 64                TransportScheme = context.Binding.Scheme,
 65                ListenUri = listenUri
 66            };
 67
 68            SecurityTokenProvider tokenProvider = tokenManager.CreateSecurityTokenProvider(serverCertRequirement);
 69            if (tokenProvider == null)
 70            {
 71                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.Cli
 72            }
 73
 74            SecurityTokenAuthenticator certificateAuthenticator =
 75                TransportSecurityHelpers.GetCertificateTokenAuthenticator(tokenManager, context.Binding.Scheme, listenUr
 76
 77            return new SslStreamSecurityUpgradeProvider(context.Binding, tokenProvider, bindingElement.RequireClientCert
 78                certificateAuthenticator, context.Binding.Scheme, bindingElement.IdentityVerifier, bindingElement.SslPro
 79        }
 80
 81        public override EndpointIdentity Identity
 82        {
 83            get
 84            {
 85                if ((_identity == null) && (ServerCertificate != null))
 86                {
 87                    _identity = SecurityUtils.GetServiceCertificateIdentity(ServerCertificate);
 88                }
 89                return _identity;
 90            }
 91        }
 92
 93        public IdentityVerifier IdentityVerifier { get; }
 94
 95        public bool RequireClientCertificate { get; }
 96
 97        public X509Certificate2 ServerCertificate { get; private set; }
 98
 99        public SecurityTokenAuthenticator ClientCertificateAuthenticator
 100        {
 101            get
 102            {
 103                if (_clientCertificateAuthenticator == null)
 104                {
 105                    _clientCertificateAuthenticator = new X509SecurityTokenAuthenticator(s_defaultX509CertificateValidat
 106                }
 107
 108                return _clientCertificateAuthenticator;
 109            }
 110        }
 111
 112        public SecurityTokenManager ClientSecurityTokenManager { get; }
 113
 114        public string Scheme { get; }
 115
 116        public SslProtocols SslProtocols { get; }
 117
 118        public override T GetProperty<T>()
 119        {
 120            if (typeof(T) == typeof(IChannelBindingProvider) || typeof(T) == typeof(IStreamUpgradeChannelBindingProvider
 121            {
 122                return (T)(object)this;
 123            }
 124            return base.GetProperty<T>();
 125        }
 126
 127        ChannelBinding IStreamUpgradeChannelBindingProvider.GetChannelBinding(StreamUpgradeAcceptor upgradeAcceptor, Cha
 128        {
 129            if (upgradeAcceptor == null)
 130            {
 131                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(upgradeAcceptor));
 132            }
 133
 134
 135            if (!(upgradeAcceptor is SslStreamSecurityUpgradeAcceptor sslupgradeAcceptor))
 136            {
 137                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(upgradeAcceptor), SR.Format(SR.Unsup
 138            }
 139
 140            if (kind != ChannelBindingKind.Endpoint)
 141            {
 142                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(kind), SR.Format(SR.StreamUpgradeUns
 143            }
 144
 145            return sslupgradeAcceptor.ChannelBinding;
 146        }
 147
 148        void IChannelBindingProvider.EnableChannelBindingSupport()
 149        {
 150            _enableChannelBinding = true;
 151        }
 152
 153
 154        bool IChannelBindingProvider.IsChannelBindingSupportEnabled => _enableChannelBinding;
 155
 156        public override StreamUpgradeAcceptor CreateUpgradeAcceptor()
 157        {
 158            ThrowIfDisposedOrNotOpen();
 159            return new SslStreamSecurityUpgradeAcceptor(this);
 160        }
 161
 162        protected override void OnAbort()
 163        {
 164            if (_clientCertificateAuthenticator != null)
 165            {
 166                SecurityUtils.AbortTokenAuthenticatorIfRequired(_clientCertificateAuthenticator);
 167            }
 168            CleanupServerCertificate();
 169        }
 170
 171        protected override async Task OnCloseAsync(CancellationToken token)
 172        {
 173            if (_clientCertificateAuthenticator != null)
 174            {
 175                await SecurityUtils.CloseTokenAuthenticatorIfRequiredAsync(_clientCertificateAuthenticator, token);
 176            }
 177            CleanupServerCertificate();
 178        }
 179
 180        private void SetupServerCertificate(SecurityToken token)
 181        {
 182            if (!(token is X509SecurityToken x509Token))
 183            {
 184                SecurityUtils.AbortTokenProviderIfRequired(_serverTokenProvider);
 185                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(
 186                    SR.InvalidTokenProvided, _serverTokenProvider.GetType(), typeof(X509SecurityToken))));
 187            }
 188            ServerCertificate = new X509Certificate2(x509Token.Certificate);
 189        }
 190
 191        private void CleanupServerCertificate()
 192        {
 193            if (ServerCertificate != null)
 194            {
 195                SecurityUtils.ResetCertificate(ServerCertificate);
 196                ServerCertificate = null;
 197            }
 198        }
 199
 200        protected override async Task OnOpenAsync(CancellationToken token)
 201        {
 202            await SecurityUtils.OpenTokenAuthenticatorIfRequiredAsync(ClientCertificateAuthenticator, token);
 203
 204            if (_serverTokenProvider != null)
 205            {
 206                await SecurityUtils.OpenTokenProviderIfRequiredAsync(_serverTokenProvider, token);
 207                // TODO: Solve issue with GetToken/GetTokenAsync needing timeouts and there is only a token available
 208                SecurityToken securityToken = await _serverTokenProvider.GetTokenAsync(token);
 209                SetupServerCertificate(securityToken);
 210                await SecurityUtils.CloseTokenProviderIfRequiredAsync(_serverTokenProvider, token);
 211                _serverTokenProvider = null;
 212            }
 213        }
 214    }
 215
 216    internal class SslStreamSecurityUpgradeAcceptor : StreamSecurityUpgradeAcceptorBase
 217    {
 218        private readonly SslStreamSecurityUpgradeProvider _parent;
 219        private SecurityMessageProperty _clientSecurity;
 220
 221        // for audit
 222        private X509Certificate2 _clientCertificate = null;
 223        private ChannelBinding _channelBindingToken;
 224
 225        public SslStreamSecurityUpgradeAcceptor(SslStreamSecurityUpgradeProvider parent)
 7226            : base(Framing.FramingUpgradeString.SslOrTls)
 227        {
 7228            _parent = parent;
 7229            _clientSecurity = new SecurityMessageProperty();
 7230        }
 231
 232        internal ChannelBinding ChannelBinding
 233        {
 234            get
 235            {
 236                Fx.Assert(IsChannelBindingSupportEnabled, "A request for the ChannelBinding is not permitted without ena
 0237                return _channelBindingToken;
 238            }
 239        }
 240
 7241        internal bool IsChannelBindingSupportEnabled => ((IChannelBindingProvider)_parent).IsChannelBindingSupportEnable
 242
 243        protected override async Task<(Stream, SecurityMessageProperty)> OnAcceptUpgradeAsync(Stream stream)
 244        {
 7245            var sslStream = new SslStream(stream, false, ValidateRemoteCertificate);
 246
 247            try
 248            {
 7249                await sslStream.AuthenticateAsServerAsync(_parent.ServerCertificate, _parent.RequireClientCertificate,
 7250                    _parent.SslProtocols, false);
 7251            }
 0252            catch (AuthenticationException exception)
 253            {
 0254                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(exception.Mes
 0255                    exception));
 256            }
 0257            catch (IOException ioException)
 258            {
 0259                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(
 0260                    SR.Format(SR.NegotiationFailedIO, ioException.Message), ioException));
 261            }
 262
 7263            SecurityMessageProperty remoteSecurity = _clientSecurity;
 264
 7265            if (IsChannelBindingSupportEnabled)
 266            {
 0267                _channelBindingToken = ChannelBindingUtility.GetToken(sslStream);
 268            }
 269
 7270            return (sslStream, remoteSecurity);
 7271        }
 272
 273        // callback from schannel
 274        private bool ValidateRemoteCertificate(object sender, X509Certificate certificate, X509Chain chain,
 275            SslPolicyErrors sslPolicyErrors)
 276        {
 7277            if (_parent.RequireClientCertificate)
 278            {
 2279                if (certificate == null)
 280                {
 0281                    return false;
 282                }
 283                // Note: add ref to handle since the caller will reset the cert after the callback return.
 2284                X509Certificate2 certificate2 = new X509Certificate2(certificate);
 2285                _clientCertificate = certificate2;
 286                try
 287                {
 2288                    SecurityToken token = new X509SecurityToken(certificate2, false);
 289                    ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies;
 2290                    var validationValueTask = _parent.ClientCertificateAuthenticator.ValidateTokenAsync(token);
 2291                    authorizationPolicies = validationValueTask.IsCompleted
 2292                        ? validationValueTask.Result
 2293                        : validationValueTask.AsTask().GetAwaiter().GetResult();
 294
 2295                    _clientSecurity = new SecurityMessageProperty
 2296                    {
 2297                        TransportToken = new SecurityTokenSpecification(token, authorizationPolicies),
 2298                        ServiceSecurityContext = new ServiceSecurityContext(authorizationPolicies)
 2299                    };
 2300                }
 301                catch (SecurityTokenException e)
 302                {
 0303                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
 0304                    return false;
 305                }
 306            }
 7307            return true;
 0308        }
 309
 310        public override SecurityMessageProperty GetRemoteSecurity()
 311        {
 7312            if (_clientSecurity.TransportToken != null)
 313            {
 2314                return _clientSecurity;
 315            }
 5316            if (_clientCertificate != null)
 317            {
 0318                return _clientSecurity;
 319            }
 5320            return base.GetRemoteSecurity();
 321        }
 322    }
 323}