< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 52
Coverable lines: 52
Total lines: 201
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 16
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%110%
CreateSessionOptions()0%220%
CreateServer(...)0%660%
CreateClient(...)100%110%
WrapShared(...)100%110%
Dispose()0%660%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Net.Security/src/System/Net/Security/TlsContext.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.Diagnostics;
 5using System.Diagnostics.CodeAnalysis;
 6
 7namespace System.Net.Security
 8{
 9    /// <summary>
 10    /// Long-lived TLS configuration. Wraps an <see cref="SslAuthenticationOptions"/>
 11    /// constructed from either <see cref="SslClientAuthenticationOptions"/> or
 12    /// <see cref="SslServerAuthenticationOptions"/>. Role (client vs. server) is
 13    /// determined by which factory is used.
 14    /// </summary>
 15    /// <remarks>
 16    /// <para>
 17    /// Holds the resolved options bag. Session caches are reused on supported
 18    /// platforms; each <see cref="TlsSession"/> gets its own native context
 19    /// allocated lazily on the first handshake call.
 20    /// </para>
 21    /// <para>
 22    /// Ownership: the <see cref="TlsContext"/> retains any
 23    /// <see cref="SslStreamCertificateContext"/> it built internally &#8212; i.e.
 24    /// when the caller supplied a raw <see cref="System.Security.Cryptography.X509Certificates.X509Certificate2"/>
 25    /// via <see cref="SslServerAuthenticationOptions.ServerCertificate"/> rather
 26    /// than a prebuilt <see cref="SslStreamCertificateContext"/>. A prebuilt
 27    /// certificate context passed via <see cref="SslServerAuthenticationOptions.ServerCertificateContext"/>
 28    /// remains owned by the caller.
 29    /// </para>
 30    /// <para>
 31    /// Lifetime: callers must keep the <see cref="TlsContext"/> alive for as long
 32    /// as any <see cref="TlsSession"/> derived from it is still in use. Native
 33    /// handles (OpenSSL <c>SSL_CTX</c>, SChannel credentials) are ref-counted at
 34    /// the native layer and stay valid for each live session, but the managed
 35    /// certificate chain retained by the context is not, mirroring the ownership
 36    /// model used by <see cref="SslStream"/>. After the context is disposed,
 37    /// attempts to create new sessions from it throw
 38    /// <see cref="ObjectDisposedException"/>.
 39    /// </para>
 40    /// </remarks>
 41    [Experimental(Experimentals.LowLevelTlsDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
 42    public sealed partial class TlsContext : IDisposable
 43    {
 44        // Non-readonly so Dispose can null it out; keeping the reference alive would
 45        // otherwise pin the (potentially large) options bag until TlsContext itself
 46        // is collected. In wedge mode the bag is owned by SslStream and Dispose
 47        // only forgets it; in owned mode Dispose also disposes the bag itself.
 48        private SslAuthenticationOptions? _options;
 49        // True when this context wraps an SslStream-owned options bag (wedge mode):
 50        // sessions share the same bag by reference and TlsContext does not dispose it,
 51        // does not allocate its own native context, and defers cred-handle lifetime to
 52        // the wrapper. False for standalone contexts created via Create(...).
 53        private readonly bool _isWedge;
 54        private readonly bool _templateHasServerOptions;
 55
 56        // SChannel credentials handle (an SSPI CredHandle from AcquireCredentialsHandle).
 57        // Owned by TlsContext so it can be shared across multiple TlsSession instances.
 58        // In wedge mode (WrapShared) SslStream owns the lifetime and we skip disposing
 59        // here to avoid double-free. Stays null on Unix — the OpenSSL SSL_CTX equivalent
 60        // lives in TlsContext.OpenSsl.cs.
 61        internal SafeFreeCredentials? CredentialsHandle;
 62
 063        private TlsContext(SslAuthenticationOptions options, bool isWedge, bool templateHasServerOptions)
 064        {
 065            _options = options;
 066            _isWedge = isWedge;
 067            _templateHasServerOptions = templateHasServerOptions;
 068        }
 69
 070        internal SslAuthenticationOptions Options => _options ?? throw new ObjectDisposedException(nameof(TlsContext));
 71
 72        // Internal accessor for the obsolete EncryptionPolicy carried in options. Not exposed
 73        // publicly: a brand-new type should not re-publish a SYSLIB0040-obsolete concept. The
 74        // setting is honored at handshake time via the options bag; internal consumers that
 75        // need to introspect it (e.g. SslStream when re-platformed on TlsSession) read it here.
 76        internal EncryptionPolicy EncryptionPolicy => Options.EncryptionPolicy;
 77
 78        // True when sessions should reuse the context's options bag directly (wedge mode).
 79        // False when each session must take a private clone before mutating any field.
 080        internal bool ShareOptions => _isWedge;
 81
 82        // True if the template was constructed with non-null server options. Sessions seed
 83        // their own per-session HasServerOptions from this and flip it in SetContext.
 084        internal bool TemplateHasServerOptions => _templateHasServerOptions;
 85
 86        // Returns a per-session options bag. For normal contexts each call returns a fresh
 87        // clone of the template so session-scoped mutations (TargetHost, SafeSslHandle,
 88        // resolved server cert, ...) don't leak between sessions. In wedge mode the bag is
 89        // owned by SslStream and we hand it out by reference. On platforms that own a
 90        // long-lived native context (e.g. OpenSSL SSL_CTX), the platform partial stamps it
 91        // onto the returned bag so the PAL can reuse it across sessions.
 92        internal SslAuthenticationOptions CreateSessionOptions()
 093        {
 094            SslAuthenticationOptions options = Options;
 095            SslAuthenticationOptions sessionOptions = _isWedge ? options : options.Clone();
 096            sessionOptions.ForceSyncPal = true;
 97            AttachSharedNativeContext(sessionOptions);
 098            return sessionOptions;
 099        }
 100
 101        // Platform hook: lets the OpenSSL partial attach the TlsContext-owned SSL_CTX to
 102        // the per-session options bag. No-op on Windows (which uses CredentialsHandle) and
 103        // on macOS/iOS/Android (no reusable native context to share).
 104        partial void AttachSharedNativeContext(SslAuthenticationOptions sessionOptions);
 105
 106        // Platform hook: lets the OpenSSL partial dispose the owned SSL_CTX. No-op elsewhere.
 107        partial void DisposeNativeContext();
 108
 0109        internal bool IsServer => Options.IsServer;
 110
 111        /// <summary>
 112        /// Creates a server-side TLS context.
 113        /// </summary>
 114        /// <param name="options">
 115        /// The server authentication options. May be a default-constructed instance
 116        /// (no server certificate, no <see cref="SslServerAuthenticationOptions.ServerCertificateSelectionCallback"/>)
 117        /// to defer server configuration: the first <see cref="TlsBufferSession.Handshake"/>
 118        /// call on a session built from that context returns
 119        /// <see cref="TlsOperationStatus.NeedsTlsContext"/> with
 120        /// <see cref="TlsSession.ClientHelloInfo"/> populated; the caller must then
 121        /// invoke <see cref="TlsSession.SetContext"/> before continuing the
 122        /// handshake. Useful for SNI-based options selection that involves I/O.
 123        /// </param>
 124        /// <returns>A new server-side <see cref="TlsContext"/>.</returns>
 125        /// <exception cref="ArgumentNullException"><paramref name="options"/> is <see langword="null"/>.</exception>
 126        public static TlsContext CreateServer(SslServerAuthenticationOptions options)
 0127        {
 0128            ArgumentNullException.ThrowIfNull(options);
 0129            SslAuthenticationOptions bag = new SslAuthenticationOptions();
 0130            bool hasServerCredentials =
 0131                options.ServerCertificate != null ||
 0132                options.ServerCertificateContext != null ||
 0133                options.ServerCertificateSelectionCallback != null;
 134
 0135            if (!hasServerCredentials)
 0136            {
 137                // Deferred: caller will resolve the credential from SNI and hand back
 138                // a completed TlsContext via TlsSession.SetContext.
 0139                bag.IsServer = true;
 0140                return new TlsContext(bag, isWedge: false, templateHasServerOptions: false);
 141            }
 142
 0143            bag.UpdateOptions(options);
 0144            return new TlsContext(bag, isWedge: false, templateHasServerOptions: true);
 0145        }
 146
 147        /// <summary>
 148        /// Creates a client-side TLS context.
 149        /// </summary>
 150        /// <param name="options">The client authentication options.</param>
 151        /// <returns>A new client-side <see cref="TlsContext"/>.</returns>
 152        /// <exception cref="ArgumentNullException"><paramref name="options"/> is <see langword="null"/>.</exception>
 153        /// <remarks>
 154        /// Peer certificate validation always runs outside the TLS state machine: after the
 155        /// handshake reaches the point at which the peer cert is available, <see cref="TlsBufferSession.Handshake"/>
 156        /// returns <see cref="TlsOperationStatus.NeedsCertificateValidation"/> and the caller
 157        /// must record a result via <see cref="TlsSession.SetRemoteCertificateValidationResult(System.Net.Security.SslP
 158        /// or <see cref="TlsSession.AcceptWithDefaultValidation"/>. Any
 159        /// <see cref="SslClientAuthenticationOptions.RemoteCertificateValidationCallback"/> set on
 160        /// <paramref name="options"/> is invoked only by <see cref="TlsSession.AcceptWithDefaultValidation"/>.
 161        /// </remarks>
 162        public static TlsContext CreateClient(SslClientAuthenticationOptions options)
 0163        {
 0164            ArgumentNullException.ThrowIfNull(options);
 0165            SslAuthenticationOptions bag = new SslAuthenticationOptions();
 0166            bag.UpdateOptions(options);
 0167            return new TlsContext(bag, isWedge: false, templateHasServerOptions: false);
 0168        }
 169
 170        // Used by SslStream's TlsSession wedge: share the existing options bag so
 171        // SNI / client-cert selection results made by SslStream are visible to the
 172        // TlsSession-driven PAL calls, and to avoid double Dispose on the bag.
 173        internal static TlsContext WrapShared(SslAuthenticationOptions sharedOptions)
 0174        {
 0175            Debug.Assert(sharedOptions != null);
 0176            return new TlsContext(sharedOptions, isWedge: true, templateHasServerOptions: sharedOptions.IsServer);
 0177        }
 178
 179        public void Dispose()
 0180        {
 0181            if (_options is null)
 0182            {
 0183                return;
 184            }
 185
 0186            if (!_isWedge)
 0187            {
 0188                CredentialsHandle?.Dispose();
 0189                CredentialsHandle = null;
 190                DisposeNativeContext();
 0191                _options.Dispose();
 0192            }
 193
 194            // In wedge mode the options bag is owned by SslStream and we must not
 195            // dispose it; but we do drop the reference so a disposed TlsContext no
 196            // longer keeps the (possibly large) bag alive.
 0197            _options = null;
 0198        }
 199    }
 200}
 201