| | | 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 | | |
| | | 4 | | using System; |
| | | 5 | | using System.Collections.ObjectModel; |
| | | 6 | | using System.Diagnostics; |
| | | 7 | | using System.IO; |
| | | 8 | | using System.Net.Security; |
| | | 9 | | using System.Security.Authentication; |
| | | 10 | | using System.Security.Authentication.ExtendedProtection; |
| | | 11 | | using System.Security.Cryptography.X509Certificates; |
| | | 12 | | using System.Threading; |
| | | 13 | | using System.Threading.Tasks; |
| | | 14 | | using CoreWCF.Description; |
| | | 15 | | using CoreWCF.IdentityModel.Policy; |
| | | 16 | | using CoreWCF.IdentityModel.Selectors; |
| | | 17 | | using CoreWCF.IdentityModel.Tokens; |
| | | 18 | | using CoreWCF.Runtime; |
| | | 19 | | using CoreWCF.Security; |
| | | 20 | | using CoreWCF.Security.Tokens; |
| | | 21 | | |
| | | 22 | | namespace 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) |
| | 7 | 226 | | : base(Framing.FramingUpgradeString.SslOrTls) |
| | | 227 | | { |
| | 7 | 228 | | _parent = parent; |
| | 7 | 229 | | _clientSecurity = new SecurityMessageProperty(); |
| | 7 | 230 | | } |
| | | 231 | | |
| | | 232 | | internal ChannelBinding ChannelBinding |
| | | 233 | | { |
| | | 234 | | get |
| | | 235 | | { |
| | | 236 | | Fx.Assert(IsChannelBindingSupportEnabled, "A request for the ChannelBinding is not permitted without ena |
| | 0 | 237 | | return _channelBindingToken; |
| | | 238 | | } |
| | | 239 | | } |
| | | 240 | | |
| | 7 | 241 | | internal bool IsChannelBindingSupportEnabled => ((IChannelBindingProvider)_parent).IsChannelBindingSupportEnable |
| | | 242 | | |
| | | 243 | | protected override async Task<(Stream, SecurityMessageProperty)> OnAcceptUpgradeAsync(Stream stream) |
| | | 244 | | { |
| | 7 | 245 | | var sslStream = new SslStream(stream, false, ValidateRemoteCertificate); |
| | | 246 | | |
| | | 247 | | try |
| | | 248 | | { |
| | 7 | 249 | | await sslStream.AuthenticateAsServerAsync(_parent.ServerCertificate, _parent.RequireClientCertificate, |
| | 7 | 250 | | _parent.SslProtocols, false); |
| | 7 | 251 | | } |
| | 0 | 252 | | catch (AuthenticationException exception) |
| | | 253 | | { |
| | 0 | 254 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(exception.Mes |
| | 0 | 255 | | exception)); |
| | | 256 | | } |
| | 0 | 257 | | catch (IOException ioException) |
| | | 258 | | { |
| | 0 | 259 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException( |
| | 0 | 260 | | SR.Format(SR.NegotiationFailedIO, ioException.Message), ioException)); |
| | | 261 | | } |
| | | 262 | | |
| | 7 | 263 | | SecurityMessageProperty remoteSecurity = _clientSecurity; |
| | | 264 | | |
| | 7 | 265 | | if (IsChannelBindingSupportEnabled) |
| | | 266 | | { |
| | 0 | 267 | | _channelBindingToken = ChannelBindingUtility.GetToken(sslStream); |
| | | 268 | | } |
| | | 269 | | |
| | 7 | 270 | | return (sslStream, remoteSecurity); |
| | 7 | 271 | | } |
| | | 272 | | |
| | | 273 | | // callback from schannel |
| | | 274 | | private bool ValidateRemoteCertificate(object sender, X509Certificate certificate, X509Chain chain, |
| | | 275 | | SslPolicyErrors sslPolicyErrors) |
| | | 276 | | { |
| | 7 | 277 | | if (_parent.RequireClientCertificate) |
| | | 278 | | { |
| | 2 | 279 | | if (certificate == null) |
| | | 280 | | { |
| | 0 | 281 | | return false; |
| | | 282 | | } |
| | | 283 | | // Note: add ref to handle since the caller will reset the cert after the callback return. |
| | 2 | 284 | | X509Certificate2 certificate2 = new X509Certificate2(certificate); |
| | 2 | 285 | | _clientCertificate = certificate2; |
| | | 286 | | try |
| | | 287 | | { |
| | 2 | 288 | | SecurityToken token = new X509SecurityToken(certificate2, false); |
| | | 289 | | ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies; |
| | 2 | 290 | | var validationValueTask = _parent.ClientCertificateAuthenticator.ValidateTokenAsync(token); |
| | 2 | 291 | | authorizationPolicies = validationValueTask.IsCompleted |
| | 2 | 292 | | ? validationValueTask.Result |
| | 2 | 293 | | : validationValueTask.AsTask().GetAwaiter().GetResult(); |
| | | 294 | | |
| | 2 | 295 | | _clientSecurity = new SecurityMessageProperty |
| | 2 | 296 | | { |
| | 2 | 297 | | TransportToken = new SecurityTokenSpecification(token, authorizationPolicies), |
| | 2 | 298 | | ServiceSecurityContext = new ServiceSecurityContext(authorizationPolicies) |
| | 2 | 299 | | }; |
| | 2 | 300 | | } |
| | | 301 | | catch (SecurityTokenException e) |
| | | 302 | | { |
| | 0 | 303 | | DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); |
| | 0 | 304 | | return false; |
| | | 305 | | } |
| | | 306 | | } |
| | 7 | 307 | | return true; |
| | 0 | 308 | | } |
| | | 309 | | |
| | | 310 | | public override SecurityMessageProperty GetRemoteSecurity() |
| | | 311 | | { |
| | 7 | 312 | | if (_clientSecurity.TransportToken != null) |
| | | 313 | | { |
| | 2 | 314 | | return _clientSecurity; |
| | | 315 | | } |
| | 5 | 316 | | if (_clientCertificate != null) |
| | | 317 | | { |
| | 0 | 318 | | return _clientSecurity; |
| | | 319 | | } |
| | 5 | 320 | | return base.GetRemoteSecurity(); |
| | | 321 | | } |
| | | 322 | | } |
| | | 323 | | } |