< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 429
Coverable lines: 429
Total lines: 1142
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 180
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/Common/src/Interop/Windows/SspiCli/SafeDeleteContext.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.Runtime.CompilerServices;
 6using System.Runtime.InteropServices;
 7using System.Security.Authentication.ExtendedProtection;
 8using Microsoft.Win32.SafeHandles;
 9
 10namespace System.Net.Security
 11{
 12    //
 13    // Implementation of handles that are dependent on DeleteSecurityContext
 14    //
 15#if DEBUG
 16    internal abstract partial class SafeDeleteContext : DebugSafeHandle
 17    {
 18#else
 19    internal abstract partial class SafeDeleteContext : SafeHandle
 20    {
 21#endif
 22        //
 23        // ATN: _handle is internal since it is used on PInvokes by other wrapper methods.
 24        //      However all such wrappers MUST manually and reliably adjust refCounter of SafeDeleteContext handle.
 25        //
 26        internal Interop.SspiCli.CredHandle _handle;
 27
 028        protected SafeDeleteContext() : base(IntPtr.Zero, true)
 029        {
 030            _handle = default;
 031        }
 32
 33        public override bool IsInvalid
 34        {
 35            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 36            get
 037            {
 038                return IsClosed || _handle.IsZero;
 039            }
 40        }
 41
 42        public override string ToString()
 043        {
 044            return _handle.ToString();
 045        }
 46    }
 47}
 48

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.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.Runtime.InteropServices;
 6using System.Security.Authentication.ExtendedProtection;
 7using System.Security.Cryptography.X509Certificates;
 8using Microsoft.Win32.SafeHandles;
 9
 10namespace System.Net.Security
 11{
 12    //
 13    // Used when working with SSPI APIs, like SafeSspiAuthDataHandle(). Holds the pointer to the auth data blob.
 14    //
 15#if DEBUG
 16    internal sealed class SafeSspiAuthDataHandle : DebugSafeHandle
 17    {
 18#else
 19    internal sealed class SafeSspiAuthDataHandle : SafeHandleZeroOrMinusOneIsInvalid
 20    {
 21#endif
 22        public SafeSspiAuthDataHandle() : base(true)
 23        {
 24        }
 25
 26        protected override bool ReleaseHandle()
 27        {
 28            return Interop.SspiCli.SspiFreeAuthIdentity(handle) == Interop.SECURITY_STATUS.OK;
 29        }
 30    }
 31
 32    //
 33    //  A set of Safe Handles that depend on native FreeContextBuffer finalizer.
 34    //
 35#if DEBUG
 36    internal abstract class SafeFreeContextBuffer : DebugSafeHandle
 37    {
 38#else
 39    internal abstract class SafeFreeContextBuffer : SafeHandleZeroOrMinusOneIsInvalid
 40    {
 41#endif
 42        protected SafeFreeContextBuffer() : base(true) { }
 43
 44        // This must be ONLY called from this file.
 45        internal void Set(IntPtr value)
 46        {
 47            this.handle = value;
 48        }
 49
 50        internal static int EnumeratePackages(out int pkgnum, out SafeFreeContextBuffer pkgArray)
 51        {
 52            int res = Interop.SspiCli.EnumerateSecurityPackagesW(out pkgnum, out SafeFreeContextBuffer_SECURITY? pkgArra
 53            pkgArray = pkgArray_SECURITY;
 54
 55            if (res != 0)
 56            {
 57                pkgArray?.SetHandleAsInvalid();
 58            }
 59
 60            return res;
 61        }
 62
 63        internal static SafeFreeContextBuffer CreateEmptyHandle()
 64        {
 65            return new SafeFreeContextBuffer_SECURITY();
 66        }
 67
 68        public static unsafe int QueryContextAttributes(SafeDeleteContext phContext, Interop.SspiCli.ContextAttribute co
 69        {
 70            bool mustRelease = false;
 71            try
 72            {
 73                phContext.DangerousAddRef(ref mustRelease);
 74                return Interop.SspiCli.QueryContextAttributesW(ref phContext._handle, contextAttribute, handle);
 75            }
 76            finally
 77            {
 78                if (mustRelease)
 79                {
 80                    phContext.DangerousRelease();
 81                }
 82            }
 83        }
 84
 85        //
 86        // After PInvoke call the method will fix the refHandle.handle with the returned value.
 87        // The caller is responsible for creating a correct SafeHandle template or null can be passed if no handle is re
 88        //
 89        // This method switches between three non-interruptible helper methods.  (This method can't be both non-interrup
 90        // reference imports from all three DLLs - doing so would cause all three DLLs to try to be bound to.)
 91        //
 92        public static unsafe int QueryContextAttributes(SafeDeleteContext phContext, Interop.SspiCli.ContextAttribute co
 93        {
 94            int status = (int)Interop.SECURITY_STATUS.InvalidHandle;
 95
 96            bool mustRelease = false;
 97            try
 98            {
 99                phContext.DangerousAddRef(ref mustRelease);
 100                status = Interop.SspiCli.QueryContextAttributesW(ref phContext._handle, contextAttribute, buffer);
 101            }
 102            finally
 103            {
 104                if (mustRelease)
 105                {
 106                    phContext.DangerousRelease();
 107                }
 108            }
 109
 110            if (status == 0 && refHandle != null)
 111            {
 112                if (refHandle is SafeFreeContextBuffer)
 113                {
 114                    ((SafeFreeContextBuffer)refHandle).Set(*(IntPtr*)buffer);
 115                }
 116                else
 117                {
 118                    Debug.Assert(false);
 119                }
 120            }
 121
 122            if (status != 0)
 123            {
 124                refHandle?.SetHandleAsInvalid();
 125            }
 126
 127            return status;
 128        }
 129
 130        public static int SetContextAttributes(
 131            SafeDeleteContext phContext,
 132            Interop.SspiCli.ContextAttribute contextAttribute, byte[] buffer)
 133        {
 134            bool mustRelease = false;
 135            try
 136            {
 137                phContext.DangerousAddRef(ref mustRelease);
 138                return Interop.SspiCli.SetContextAttributesW(ref phContext._handle, contextAttribute, buffer, buffer.Len
 139            }
 140            finally
 141            {
 142                if (mustRelease)
 143                {
 144                    phContext.DangerousRelease();
 145                }
 146            }
 147        }
 148    }
 149
 150    internal sealed class SafeFreeContextBuffer_SECURITY : SafeFreeContextBuffer
 151    {
 152        public SafeFreeContextBuffer_SECURITY() : base() { }
 153
 154        protected override bool ReleaseHandle()
 155        {
 156            return Interop.SspiCli.FreeContextBuffer(handle) == 0;
 157        }
 158    }
 159
 160    //
 161    // Implementation of handles required CertFreeCertificateContext
 162    //
 163#if DEBUG
 164    internal sealed class SafeFreeCertContext : DebugSafeHandle
 165    {
 166#else
 167    internal sealed class SafeFreeCertContext : SafeHandleZeroOrMinusOneIsInvalid
 168    {
 169#endif
 170
 171        public SafeFreeCertContext() : base(true) { }
 172
 173        // This must be ONLY called from this file.
 174        internal void Set(IntPtr value)
 175        {
 176            this.handle = value;
 177        }
 178
 179        protected override bool ReleaseHandle()
 180        {
 181            Interop.Crypt32.CertFreeCertificateContext(handle);
 182            return true;
 183        }
 184    }
 185
 186    //
 187    // Implementation of handles dependable on FreeCredentialsHandle
 188    //
 189#if DEBUG
 190    internal abstract class SafeFreeCredentials : DebugSafeHandle
 191    {
 192#else
 193    internal abstract class SafeFreeCredentials : SafeHandle
 194    {
 195#endif
 196
 197        internal DateTime _expiry;
 198        internal Interop.SspiCli.CredHandle _handle;    //should be always used as by ref in PInvokes parameters
 199
 200        protected SafeFreeCredentials() : base(IntPtr.Zero, true)
 201        {
 202            _handle = default;
 203            _expiry = DateTime.MaxValue;
 204        }
 205
 206        public override bool IsInvalid
 207        {
 208            get { return IsClosed || _handle.IsZero; }
 209        }
 210
 211        public DateTime Expiry => _expiry;
 212
 213        public static unsafe int AcquireDefaultCredential(
 214            string package,
 215            Interop.SspiCli.CredentialUse intent,
 216            out SafeFreeCredentials outCredential)
 217        {
 218            int errorCode = -1;
 219            long timeStamp;
 220
 221            outCredential = new SafeFreeCredential_SECURITY();
 222
 223            errorCode = Interop.SspiCli.AcquireCredentialsHandleW(
 224                            null,
 225                            package,
 226                            (int)intent,
 227                            null,
 228                            IntPtr.Zero,
 229                            null,
 230                            null,
 231                            ref outCredential._handle,
 232                            out timeStamp);
 233
 234            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, $"{nameof(Interop.SspiCli.AcquireCredentialsHa
 235
 236            if (errorCode != 0)
 237            {
 238                outCredential.SetHandleAsInvalid();
 239            }
 240
 241            return errorCode;
 242        }
 243
 244        public static unsafe int AcquireCredentialsHandle(
 245            string package,
 246            Interop.SspiCli.CredentialUse intent,
 247            ref SafeSspiAuthDataHandle authdata,
 248            out SafeFreeCredentials outCredential)
 249        {
 250            outCredential = new SafeFreeCredential_SECURITY();
 251            int errorCode = Interop.SspiCli.AcquireCredentialsHandleW(
 252                            null,
 253                            package,
 254                            (int)intent,
 255                            null,
 256                            authdata,
 257                            null,
 258                            null,
 259                            ref outCredential._handle,
 260                            out _);
 261
 262            if (errorCode != 0)
 263            {
 264                outCredential.SetHandleAsInvalid();
 265            }
 266
 267            return errorCode;
 268        }
 269
 270        public static unsafe int AcquireCredentialsHandle(
 271            string package,
 272            Interop.SspiCli.CredentialUse intent,
 273            Interop.SspiCli.SCHANNEL_CRED* authdata,
 274            out SafeFreeCredentials outCredential)
 275        {
 276            int errorCode = -1;
 277
 278            outCredential = new SafeFreeCredential_SECURITY();
 279
 280            errorCode = Interop.SspiCli.AcquireCredentialsHandleW(
 281                                null,
 282                                package,
 283                                (int)intent,
 284                                null,
 285                                authdata,
 286                                null,
 287                                null,
 288                                ref outCredential._handle,
 289                                out _);
 290
 291            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, $"{nameof(Interop.SspiCli.AcquireCredentialsHa
 292
 293            if (errorCode != 0)
 294            {
 295                outCredential.SetHandleAsInvalid();
 296            }
 297
 298            return errorCode;
 299        }
 300
 301        public static unsafe int AcquireCredentialsHandle(
 302            string package,
 303            Interop.SspiCli.CredentialUse intent,
 304            Interop.SspiCli.SCH_CREDENTIALS* authdata,
 305            out SafeFreeCredentials outCredential)
 306        {
 307            long timeStamp;
 308
 309            outCredential = new SafeFreeCredential_SECURITY();
 310
 311            int errorCode = Interop.SspiCli.AcquireCredentialsHandleW(
 312                                null,
 313                                package,
 314                                (int)intent,
 315                                null,
 316                                authdata,
 317                                null,
 318                                null,
 319                                ref outCredential._handle,
 320                                out timeStamp);
 321
 322            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, $"{nameof(Interop.SspiCli.AcquireCredentialsHa
 323
 324            if (errorCode != 0)
 325            {
 326                outCredential.SetHandleAsInvalid();
 327            }
 328
 329            return errorCode;
 330        }
 331
 332    }
 333
 334    internal sealed class SafeFreeCredential_SECURITY : SafeFreeCredentials
 335    {
 336#pragma warning disable 0649
 337        // This is used only by SslStream but it is included elsewhere
 338        public bool HasLocalCertificate;
 339#pragma warning restore 0649
 340        public SafeFreeCredential_SECURITY() : base() { }
 341
 342        protected override bool ReleaseHandle()
 343        {
 344            return Interop.SspiCli.FreeCredentialsHandle(ref _handle) == 0;
 345        }
 346    }
 347
 348    //
 349    // Implementation of handles that are dependent on DeleteSecurityContext
 350    //
 351#if DEBUG
 352    internal abstract partial class SafeDeleteContext : DebugSafeHandle
 353    {
 354#else
 355    internal abstract partial class SafeDeleteContext : SafeHandle
 356    {
 357#endif
 358        protected SafeFreeCredentials? _EffectiveCredential;
 359
 360        //-------------------------------------------------------------------
 361        internal static unsafe int InitializeSecurityContext(
 362            ref SafeFreeCredentials? inCredentials,
 363            ref SafeDeleteSslContext? refContext,
 364            string? targetName,
 365            Interop.SspiCli.ContextFlags inFlags,
 366            Interop.SspiCli.Endianness endianness,
 367            ref InputSecurityBuffers inSecBuffers,
 368            ref ProtocolToken outToken,
 369            ref Interop.SspiCli.ContextFlags outFlags)
 0370        {
 0371            ArgumentNullException.ThrowIfNull(inCredentials);
 372
 0373            Debug.Assert(inSecBuffers.Count <= 3);
 0374            Interop.SspiCli.SecBufferDesc inSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(inSecBuffers.Co
 0375            Interop.SspiCli.SecBufferDesc outSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(1);
 376
 377            // Actually, this is returned in outFlags.
 0378            bool isSspiAllocated = (inFlags & Interop.SspiCli.ContextFlags.AllocateMemory) != 0 ? true : false;
 379
 0380            int errorCode = -1;
 381
 0382            bool isContextAbsent = true;
 0383            if (refContext != null)
 0384            {
 0385                isContextAbsent = refContext._handle.IsZero;
 0386            }
 387
 388            // Optional output buffer that may need to be freed.
 0389            IntPtr outoutBuffer = IntPtr.Zero;
 390            try
 0391            {
 0392                Span<Interop.SspiCli.SecBuffer> inUnmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[3];
 393
 0394                fixed (void* inUnmanagedBufferPtr = inUnmanagedBuffer)
 0395                fixed (void* pinnedToken0 = inSecBuffers._item0.Token)
 0396                fixed (void* pinnedToken1 = inSecBuffers._item1.Token)
 0397                fixed (void* pinnedToken2 = inSecBuffers._item2.Token)
 0398                {
 399                    // Fix Descriptor pointer that points to unmanaged SecurityBuffers.
 0400                    inSecurityBufferDescriptor.pBuffers = inUnmanagedBufferPtr;
 401                    // Updated pvBuffer with pinned address. UnmanagedToken takes precedence.
 0402                    if (inSecBuffers.Count > 2)
 0403                    {
 0404                        inUnmanagedBuffer[2].BufferType = inSecBuffers._item2.Type;
 0405                        if (inSecBuffers._item2.UnmanagedToken != null)
 0406                        {
 0407                            Debug.Assert(inSecBuffers._item2.Type == SecurityBufferType.SECBUFFER_CHANNEL_BINDINGS);
 0408                            inUnmanagedBuffer[2].pvBuffer = (IntPtr)inSecBuffers._item2.UnmanagedToken.DangerousGetHandl
 0409                            inUnmanagedBuffer[2].cbBuffer = ((ChannelBinding)inSecBuffers._item2.UnmanagedToken).Size;
 0410                        }
 411                        else
 0412                        {
 0413                            inUnmanagedBuffer[2].cbBuffer = inSecBuffers._item2.Token.Length;
 0414                            inUnmanagedBuffer[2].pvBuffer = (IntPtr)pinnedToken2;
 0415                        }
 416
 0417                    }
 418
 0419                    if (inSecBuffers.Count > 1)
 0420                    {
 0421                        inUnmanagedBuffer[1].BufferType = inSecBuffers._item1.Type;
 0422                        if (inSecBuffers._item1.UnmanagedToken != null)
 0423                        {
 0424                            Debug.Assert(inSecBuffers._item1.Type == SecurityBufferType.SECBUFFER_CHANNEL_BINDINGS);
 0425                            inUnmanagedBuffer[1].pvBuffer = (IntPtr)inSecBuffers._item1.UnmanagedToken.DangerousGetHandl
 0426                            inUnmanagedBuffer[1].cbBuffer = ((ChannelBinding)inSecBuffers._item1.UnmanagedToken).Size;
 0427                        }
 428                        else
 0429                        {
 0430                            inUnmanagedBuffer[1].cbBuffer = inSecBuffers._item1.Token.Length;
 0431                            inUnmanagedBuffer[1].pvBuffer = (IntPtr)pinnedToken1;
 0432                        }
 0433                    }
 434
 0435                    if (inSecBuffers.Count > 0)
 0436                    {
 0437                        inUnmanagedBuffer[0].BufferType = inSecBuffers._item0.Type;
 0438                        if (inSecBuffers._item0.UnmanagedToken != null)
 0439                        {
 0440                            Debug.Assert(inSecBuffers._item0.Type == SecurityBufferType.SECBUFFER_CHANNEL_BINDINGS);
 0441                            inUnmanagedBuffer[0].pvBuffer = (IntPtr)inSecBuffers._item0.UnmanagedToken.DangerousGetHandl
 0442                            inUnmanagedBuffer[0].cbBuffer = ((ChannelBinding)inSecBuffers._item0.UnmanagedToken).Size;
 0443                        }
 444                        else
 0445                        {
 0446                            inUnmanagedBuffer[0].cbBuffer = inSecBuffers._item0.Token.Length;
 0447                            inUnmanagedBuffer[0].pvBuffer = (IntPtr)pinnedToken0;
 0448                        }
 0449                    }
 450
 0451                    fixed (byte* pinnedOutBytes = outToken.Payload)
 0452                    {
 453                        // Fix Descriptor pointer that points to unmanaged SecurityBuffers.
 0454                        Interop.SspiCli.SecBuffer outUnmanagedBuffer = default;
 0455                        outSecurityBufferDescriptor.pBuffers = &outUnmanagedBuffer;
 0456                        outUnmanagedBuffer.cbBuffer = outToken.Size;
 0457                        outUnmanagedBuffer.BufferType = SecurityBufferType.SECBUFFER_TOKEN;
 0458                        outUnmanagedBuffer.pvBuffer = outToken.Payload == null || outToken.Size == 0 ?
 0459                            IntPtr.Zero :
 0460                            (IntPtr)(pinnedOutBytes);
 461
 0462                        if (refContext == null || refContext.IsInvalid)
 0463                        {
 464                            // Previous versions unconditionally built a new "refContext" here, but would pass
 465                            // incorrect arguments to InitializeSecurityContextW in cases where an "contextHandle" was
 466                            // already present and non-zero.
 0467                            if (isContextAbsent)
 0468                            {
 0469                                refContext?.Dispose();
 0470                                refContext = new SafeDeleteSslContext();
 0471                            }
 0472                        }
 473
 0474                        fixed (char* namePtr = targetName)
 0475                        {
 0476                            errorCode = MustRunInitializeSecurityContext(
 0477                                            ref inCredentials,
 0478                                            isContextAbsent,
 0479                                            (byte*)namePtr,
 0480                                            inFlags,
 0481                                            endianness,
 0482                                            &inSecurityBufferDescriptor,
 0483                                            refContext!,
 0484                                            ref outSecurityBufferDescriptor,
 0485                                            ref outFlags,
 0486                                            null);
 487
 0488                            if (isSspiAllocated)
 0489                            {
 0490                                outoutBuffer = outUnmanagedBuffer.pvBuffer;
 0491                            }
 492
 493                            // Get unmanaged buffer with index 0 as the only one passed into PInvoke.
 0494                            if (isSspiAllocated)
 0495                            {
 0496                                if (outUnmanagedBuffer.cbBuffer > 0)
 0497                                {
 0498                                    outToken.EnsureAvailableSpace(outUnmanagedBuffer.cbBuffer);
 0499                                    new Span<byte>((byte*)outUnmanagedBuffer.pvBuffer, outUnmanagedBuffer.cbBuffer).Copy
 0500                                }
 0501                            }
 0502                            outToken.Size = outUnmanagedBuffer.cbBuffer;
 503
 504                            // In some cases schannel may not process all the given data.
 505                            // and it will return them back as SECBUFFER_EXTRA, expecting caller to
 506                            // feed them in again. Propagate this information back up.
 0507                            if (inSecBuffers.Count > 1 && inUnmanagedBuffer[1].BufferType == SecurityBufferType.SECBUFFE
 0508                            {
 0509                                inSecBuffers._item1.Type = inUnmanagedBuffer[1].BufferType;
 510
 511                                // since SecurityBuffer type does not have separate Length field,
 512                                // we point to the unused portion of the input buffer.
 0513                                Debug.Assert(inSecBuffers._item0.Token.Length > inUnmanagedBuffer[1].cbBuffer);
 0514                                inSecBuffers._item1.Token = inSecBuffers._item0.Token.Slice(inSecBuffers._item0.Token.Le
 0515                            }
 0516                        }
 0517                    }
 0518                }
 0519            }
 520            finally
 0521            {
 0522                if (outoutBuffer != IntPtr.Zero)
 0523                {
 0524                    Interop.SspiCli.FreeContextBuffer(outoutBuffer);
 0525                }
 0526            }
 527
 0528            return errorCode;
 0529        }
 530
 531        //
 532        // After PInvoke call the method will fix the handleTemplate.handle with the returned value.
 533        // The caller is responsible for creating a correct SafeFreeContextBuffer_XXX flavor or null can be passed if no
 534        //
 535        private static unsafe int MustRunInitializeSecurityContext(
 536            ref SafeFreeCredentials inCredentials,
 537            bool isContextAbsent,
 538            byte* targetName,
 539            Interop.SspiCli.ContextFlags inFlags,
 540            Interop.SspiCli.Endianness endianness,
 541            Interop.SspiCli.SecBufferDesc* inputBuffer,
 542            SafeDeleteContext outContext,
 543            ref Interop.SspiCli.SecBufferDesc outputBuffer,
 544            ref Interop.SspiCli.ContextFlags attributes,
 545            SafeFreeContextBuffer? handleTemplate)
 0546        {
 0547            int errorCode = (int)Interop.SECURITY_STATUS.InvalidHandle;
 548
 0549            bool mustReleaseCredentials = false;
 0550            bool mustReleaseOutContext = false;
 551            try
 0552            {
 0553                inCredentials.DangerousAddRef(ref mustReleaseCredentials);
 0554                outContext.DangerousAddRef(ref mustReleaseOutContext);
 555
 0556                Interop.SspiCli.CredHandle credentialHandle = inCredentials._handle;
 557
 558                long timeStamp;
 559
 560                // Now that "outContext" (or "refContext" by the caller) references an actual handle (and cannot
 561                // be closed until it is released below), point "inContextPtr" to its embedded handle (or
 562                // null if the embedded handle has not yet been initialized).
 0563                Interop.SspiCli.CredHandle contextHandle = outContext._handle;
 0564                void* inContextPtr = contextHandle.IsZero ? null : &contextHandle;
 565
 566                // The "isContextAbsent" supplied by the caller is generally correct but was computed without proper
 567                // synchronization. Rewrite the indicator now that the final "inContext" is known, update if necessary.
 0568                isContextAbsent = (inContextPtr == null);
 569
 0570                errorCode = Interop.SspiCli.InitializeSecurityContextW(
 0571                                ref credentialHandle,
 0572                                inContextPtr,
 0573                                targetName,
 0574                                inFlags,
 0575                                0,
 0576                                endianness,
 0577                                inputBuffer,
 0578                                0,
 0579                                ref outContext._handle,
 0580                                ref outputBuffer,
 0581                                ref attributes,
 0582                                out timeStamp);
 0583            }
 584            finally
 0585            {
 586                //
 587                // When a credential handle is first associated with the context we keep credential
 588                // ref count bumped up to ensure ordered finalization.
 589                // If the credential handle has been changed we de-ref the old one and associate the
 590                //  context with the new cred handle but only if the call was successful.
 0591                if (outContext._EffectiveCredential != inCredentials && (errorCode & 0x80000000) == 0)
 0592                {
 593                    // Disassociate the previous credential handle
 0594                    outContext._EffectiveCredential?.DangerousRelease();
 0595                    outContext._EffectiveCredential = inCredentials;
 0596                }
 0597                else if (mustReleaseCredentials)
 0598                {
 0599                    inCredentials.DangerousRelease();
 0600                }
 601
 0602                if (mustReleaseOutContext)
 0603                {
 0604                    outContext.DangerousRelease();
 0605                }
 0606            }
 607
 608            // The idea is that SSPI has allocated a block and filled up outUnmanagedBuffer+8 slot with the pointer.
 0609            if (handleTemplate != null)
 0610            {
 611                //ATTN: on 64 BIT that is still +8 cause of 2* c++ unsigned long == 8 bytes
 0612                handleTemplate.Set(((Interop.SspiCli.SecBuffer*)outputBuffer.pBuffers)->pvBuffer);
 0613                if (handleTemplate.IsInvalid)
 0614                {
 0615                    handleTemplate.SetHandleAsInvalid();
 0616                }
 0617            }
 618
 0619            if (isContextAbsent && (errorCode & 0x80000000) != 0)
 0620            {
 621                // an error on the first call, need to set the out handle to invalid value
 0622                outContext._handle.SetToInvalid();
 0623            }
 624
 0625            return errorCode;
 0626        }
 627
 628        //-------------------------------------------------------------------
 629        internal static unsafe int AcceptSecurityContext(
 630            ref SafeFreeCredentials? inCredentials,
 631            ref SafeDeleteSslContext? refContext,
 632            Interop.SspiCli.ContextFlags inFlags,
 633            Interop.SspiCli.Endianness endianness,
 634            ref InputSecurityBuffers inSecBuffers,
 635            ref ProtocolToken outToken,
 636            ref Interop.SspiCli.ContextFlags outFlags)
 0637        {
 0638            ArgumentNullException.ThrowIfNull(inCredentials);
 639
 0640            Debug.Assert(inSecBuffers.Count <= 3);
 0641            Interop.SspiCli.SecBufferDesc inSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(inSecBuffers.Co
 0642            Interop.SspiCli.SecBufferDesc outSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(count: 2);
 643
 644            // Actually, this is returned in outFlags.
 0645            bool isSspiAllocated = (inFlags & Interop.SspiCli.ContextFlags.AllocateMemory) != 0 ? true : false;
 646
 0647            int errorCode = -1;
 648
 0649            bool isContextAbsent = true;
 0650            if (refContext != null)
 0651            {
 0652                isContextAbsent = refContext._handle.IsZero;
 0653            }
 654
 0655            Span<Interop.SspiCli.SecBuffer> outUnmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2];
 0656            outUnmanagedBuffer[1].pvBuffer = IntPtr.Zero;
 657            try
 0658            {
 659                // Allocate always maximum to allow better code optimization.
 0660                Span<Interop.SspiCli.SecBuffer> inUnmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[3];
 661
 0662                fixed (void* inUnmanagedBufferPtr = inUnmanagedBuffer)
 0663                fixed (void* outUnmanagedBufferPtr = outUnmanagedBuffer)
 0664                fixed (void* pinnedToken0 = inSecBuffers._item0.Token)
 0665                fixed (void* pinnedToken1 = inSecBuffers._item1.Token)
 0666                fixed (void* pinnedToken2 = inSecBuffers._item2.Token)
 0667                {
 0668                    inSecurityBufferDescriptor.pBuffers = inUnmanagedBufferPtr;
 669                    // Updated pvBuffer with pinned address. UnmanagedToken takes precedence.
 0670                    if (inSecBuffers.Count > 2)
 0671                    {
 0672                        inUnmanagedBuffer[2].BufferType = inSecBuffers._item2.Type;
 0673                        if (inSecBuffers._item2.UnmanagedToken != null)
 0674                        {
 0675                            Debug.Assert(inSecBuffers._item2.Type == SecurityBufferType.SECBUFFER_CHANNEL_BINDINGS);
 0676                            inUnmanagedBuffer[2].pvBuffer = (IntPtr)inSecBuffers._item2.UnmanagedToken.DangerousGetHandl
 0677                            inUnmanagedBuffer[2].cbBuffer = ((ChannelBinding)inSecBuffers._item2.UnmanagedToken).Size;
 0678                        }
 679                        else
 0680                        {
 0681                            inUnmanagedBuffer[2].cbBuffer = inSecBuffers._item2.Token.Length;
 0682                            inUnmanagedBuffer[2].pvBuffer = (IntPtr)pinnedToken2;
 0683                        }
 684
 0685                    }
 686
 0687                    if (inSecBuffers.Count > 1)
 0688                    {
 0689                        inUnmanagedBuffer[1].BufferType = inSecBuffers._item1.Type;
 0690                        if (inSecBuffers._item1.UnmanagedToken != null)
 0691                        {
 0692                            Debug.Assert(inSecBuffers._item1.Type == SecurityBufferType.SECBUFFER_CHANNEL_BINDINGS);
 0693                            inUnmanagedBuffer[1].pvBuffer = (IntPtr)inSecBuffers._item1.UnmanagedToken.DangerousGetHandl
 0694                            inUnmanagedBuffer[1].cbBuffer = ((ChannelBinding)inSecBuffers._item1.UnmanagedToken).Size;
 0695                        }
 696                        else
 0697                        {
 0698                            inUnmanagedBuffer[1].cbBuffer = inSecBuffers._item1.Token.Length;
 0699                            inUnmanagedBuffer[1].pvBuffer = (IntPtr)pinnedToken1;
 0700                        }
 0701                    }
 702
 0703                    if (inSecBuffers.Count > 0)
 0704                    {
 0705                        inUnmanagedBuffer[0].BufferType = inSecBuffers._item0.Type;
 0706                        if (inSecBuffers._item0.UnmanagedToken != null)
 0707                        {
 0708                            Debug.Assert(inSecBuffers._item0.Type == SecurityBufferType.SECBUFFER_CHANNEL_BINDINGS);
 0709                            inUnmanagedBuffer[0].pvBuffer = (IntPtr)inSecBuffers._item0.UnmanagedToken.DangerousGetHandl
 0710                            inUnmanagedBuffer[0].cbBuffer = ((ChannelBinding)inSecBuffers._item0.UnmanagedToken).Size;
 0711                        }
 712                        else
 0713                        {
 0714                            inUnmanagedBuffer[0].cbBuffer = inSecBuffers._item0.Token.Length;
 0715                            inUnmanagedBuffer[0].pvBuffer = (IntPtr)pinnedToken0;
 0716                        }
 0717                    }
 718
 0719                    fixed (byte* pinnedOutBytes = outToken.Payload)
 0720                    {
 721                        // Fix Descriptor pointer that points to unmanaged SecurityBuffers.
 0722                        outSecurityBufferDescriptor.pBuffers = outUnmanagedBufferPtr;
 723
 724                        // Copy the SecurityBuffer content into unmanaged place holder.
 0725                        outUnmanagedBuffer[0].cbBuffer = outToken.Size;
 0726                        outUnmanagedBuffer[0].BufferType = SecurityBufferType.SECBUFFER_TOKEN;
 0727                        outUnmanagedBuffer[0].pvBuffer = outToken.Payload == null || outToken.Payload.Length == 0 ?
 0728                            IntPtr.Zero :
 0729                            (IntPtr)(pinnedOutBytes);
 730
 0731                        outUnmanagedBuffer[1].cbBuffer = 0;
 0732                        outUnmanagedBuffer[1].BufferType = SecurityBufferType.SECBUFFER_ALERT;
 733
 0734                        if (refContext == null || refContext.IsInvalid)
 0735                        {
 736                            // Previous versions unconditionally built a new "refContext" here, but would pass
 737                            // incorrect arguments to AcceptSecurityContext in cases where an "contextHandle" was
 738                            // already present and non-zero.
 0739                            if (isContextAbsent)
 0740                                refContext = new SafeDeleteSslContext();
 0741                        }
 742
 0743                        errorCode = MustRunAcceptSecurityContext_SECURITY(
 0744                                        ref inCredentials,
 0745                                        isContextAbsent,
 0746                                        &inSecurityBufferDescriptor,
 0747                                        inFlags,
 0748                                        endianness,
 0749                                        refContext!,
 0750                                        ref outSecurityBufferDescriptor,
 0751                                        ref outFlags,
 0752                                        null);
 753
 754                        // No data written out but there is Alert
 0755                        int index = outUnmanagedBuffer[0].cbBuffer == 0 && outUnmanagedBuffer[1].cbBuffer > 0 ? 1 : 0;
 756
 0757                        int length = outUnmanagedBuffer[index].cbBuffer;
 0758                        if (isSspiAllocated && length > 0)
 0759                        {
 0760                            outToken.EnsureAvailableSpace(length);
 0761                            new Span<byte>((byte*)outUnmanagedBuffer[index].pvBuffer, length).CopyTo(outToken.AvailableS
 0762                        }
 0763                        outToken.Size = length;
 764
 765                        // In some cases schannel may not process all the given data.
 766                        // and it will return them back as SECBUFFER_EXTRA, expecting caller to
 767                        // feed them in again. Propagate this information back up.
 0768                        if (inSecBuffers.Count > 1 && inUnmanagedBuffer[1].BufferType == SecurityBufferType.SECBUFFER_EX
 0769                        {
 0770                            inSecBuffers._item1.Type = inUnmanagedBuffer[1].BufferType;
 771
 772                            // since SecurityBuffer type does not have separate Length field,
 773                            // we point to the unused portion of the input buffer.
 0774                            Debug.Assert(inSecBuffers._item0.Token.Length > inUnmanagedBuffer[1].cbBuffer);
 0775                            inSecBuffers._item1.Token = inSecBuffers._item0.Token.Slice(inSecBuffers._item0.Token.Length
 0776                        }
 0777                    }
 0778                }
 0779            }
 780            finally
 0781            {
 0782                if (isSspiAllocated && outUnmanagedBuffer[0].pvBuffer != IntPtr.Zero)
 0783                {
 0784                    Interop.SspiCli.FreeContextBuffer(outUnmanagedBuffer[0].pvBuffer);
 0785                }
 786
 0787                if (outUnmanagedBuffer[1].pvBuffer != IntPtr.Zero)
 0788                {
 0789                    Interop.SspiCli.FreeContextBuffer(outUnmanagedBuffer[1].pvBuffer);
 0790                }
 0791            }
 792
 0793            return errorCode;
 0794        }
 795
 796        //
 797        // After PInvoke call the method will fix the handleTemplate.handle with the returned value.
 798        // The caller is responsible for creating a correct SafeFreeContextBuffer_XXX flavor or null can be passed if no
 799        //
 800        private static unsafe int MustRunAcceptSecurityContext_SECURITY(
 801            ref SafeFreeCredentials inCredentials,
 802            bool isContextAbsent,
 803            Interop.SspiCli.SecBufferDesc* inputBuffer,
 804            Interop.SspiCli.ContextFlags inFlags,
 805            Interop.SspiCli.Endianness endianness,
 806            SafeDeleteContext outContext,
 807            ref Interop.SspiCli.SecBufferDesc outputBuffer,
 808            ref Interop.SspiCli.ContextFlags outFlags,
 809            SafeFreeContextBuffer? handleTemplate)
 0810        {
 0811            int errorCode = (int)Interop.SECURITY_STATUS.InvalidHandle;
 812
 0813            bool mustReleaseCredentials = false;
 0814            bool mustReleaseOutContext = false;
 815            // Run the body of this method as a non-interruptible block.
 816            try
 0817            {
 0818                inCredentials.DangerousAddRef(ref mustReleaseCredentials);
 0819                outContext.DangerousAddRef(ref mustReleaseOutContext);
 820
 0821                Interop.SspiCli.CredHandle credentialHandle = inCredentials._handle;
 822                long timeStamp;
 823
 824                // Now that "outContext" (or "refContext" by the caller) references an actual handle (and cannot
 825                // be closed until it is released below), point "inContextPtr" to its embedded handle (or
 826                // null if the embedded handle has not yet been initialized).
 0827                Interop.SspiCli.CredHandle contextHandle = outContext._handle;
 0828                void* inContextPtr = contextHandle.IsZero ? null : &contextHandle;
 829
 830                // The "isContextAbsent" supplied by the caller is generally correct but was computed without proper
 831                // synchronization. Rewrite the indicator now that the final "inContext" is known, update if necessary.
 0832                isContextAbsent = (inContextPtr == null);
 833
 0834                errorCode = Interop.SspiCli.AcceptSecurityContext(
 0835                                ref credentialHandle,
 0836                                inContextPtr,
 0837                                inputBuffer,
 0838                                inFlags,
 0839                                endianness,
 0840                                ref outContext._handle,
 0841                                ref outputBuffer,
 0842                                ref outFlags,
 0843                                out timeStamp);
 0844            }
 845            finally
 0846            {
 847                //
 848                // When a credential handle is first associated with the context we keep credential
 849                // ref count bumped up to ensure ordered finalization.
 850                // If the credential handle has been changed we de-ref the old one and associate the
 851                //  context with the new cred handle but only if the call was successful.
 0852                if (outContext._EffectiveCredential != inCredentials && (errorCode & 0x80000000) == 0)
 0853                {
 854                    // Disassociate the previous credential handle.
 0855                    outContext._EffectiveCredential?.DangerousRelease();
 0856                    outContext._EffectiveCredential = inCredentials;
 0857                }
 0858                else if (mustReleaseCredentials)
 0859                {
 0860                    inCredentials.DangerousRelease();
 0861                }
 862
 0863                if (mustReleaseOutContext)
 0864                {
 0865                    outContext.DangerousRelease();
 0866                }
 0867            }
 868
 869            // The idea is that SSPI has allocated a block and filled up outUnmanagedBuffer+8 slot with the pointer.
 0870            if (handleTemplate != null)
 0871            {
 872                //ATTN: on 64 BIT that is still +8 cause of 2* c++ unsigned long == 8 bytes.
 0873                handleTemplate.Set(((Interop.SspiCli.SecBuffer*)outputBuffer.pBuffers)->pvBuffer);
 0874                if (handleTemplate.IsInvalid)
 0875                {
 0876                    handleTemplate.SetHandleAsInvalid();
 0877                }
 0878            }
 879
 0880            if (isContextAbsent && (errorCode & 0x80000000) != 0)
 0881            {
 882                // An error on the first call, need to set the out handle to invalid value.
 0883                outContext._handle.SetToInvalid();
 0884            }
 885
 0886            return errorCode;
 0887        }
 888
 889        internal static unsafe int CompleteAuthToken(
 890            ref SafeDeleteSslContext? refContext,
 891            in InputSecurityBuffer inSecBuffer)
 0892        {
 0893            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, $"refContext = {refContext}");
 894
 0895            var inSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(1);
 0896            int errorCode = (int)Interop.SECURITY_STATUS.InvalidHandle;
 897
 0898            Interop.SspiCli.SecBuffer inUnmanagedBuffer = default;
 0899            inSecurityBufferDescriptor.pBuffers = &inUnmanagedBuffer;
 0900            fixed (byte* pinnedToken = inSecBuffer.Token)
 0901            {
 0902                Debug.Assert(inSecBuffer.UnmanagedToken != null);
 0903                inUnmanagedBuffer.cbBuffer = inSecBuffer.Token.Length;
 0904                inUnmanagedBuffer.BufferType = inSecBuffer.Type;
 0905                inUnmanagedBuffer.pvBuffer =
 0906                    inSecBuffer.Token.IsEmpty ? IntPtr.Zero : (IntPtr)pinnedToken;
 907
 0908                Interop.SspiCli.CredHandle contextHandle = refContext != null ? refContext._handle : default;
 0909                if (refContext == null || refContext.IsInvalid)
 0910                {
 911                    // Previous versions unconditionally built a new "refContext" here, but would pass
 912                    // incorrect arguments to CompleteAuthToken in cases where a nonzero "contextHandle" was
 913                    // already present. In these cases, allow the "refContext" to flow through unmodified
 914                    // (which will generate an ObjectDisposedException below). In all other cases, continue to
 915                    // build a new "refContext" in an attempt to maximize compat.
 0916                    if (contextHandle.IsZero)
 0917                    {
 0918                        refContext = new SafeDeleteSslContext();
 0919                    }
 0920                }
 921
 0922                bool gotRef = false;
 923                try
 0924                {
 0925                    refContext!.DangerousAddRef(ref gotRef);
 0926                    errorCode = Interop.SspiCli.CompleteAuthToken(contextHandle.IsZero ? null : &contextHandle, ref inSe
 0927                }
 928                finally
 0929                {
 0930                    if (gotRef)
 0931                    {
 0932                        refContext!.DangerousRelease();
 0933                    }
 0934                }
 0935            }
 936
 0937            return errorCode;
 0938        }
 939
 940        internal static unsafe int ApplyControlToken(
 941            ref SafeDeleteSslContext? refContext,
 942            in SecurityBuffer inSecBuffer)
 0943        {
 0944            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, $"refContext = {refContext}, inSecBuffer = {in
 945
 0946            int errorCode = (int)Interop.SECURITY_STATUS.InvalidHandle;
 947
 948            // Fix Descriptor pointer that points to unmanaged SecurityBuffers.
 0949            fixed (byte* pinnedInSecBufferToken = inSecBuffer.token)
 0950            {
 0951                var inSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(1);
 0952                Interop.SspiCli.SecBuffer inUnmanagedBuffer = default;
 0953                inSecurityBufferDescriptor.pBuffers = &inUnmanagedBuffer;
 0954                inUnmanagedBuffer.cbBuffer = inSecBuffer.size;
 0955                inUnmanagedBuffer.BufferType = inSecBuffer.type;
 956
 957                // Use the unmanaged token if it's not null; otherwise use the managed buffer.
 0958                inUnmanagedBuffer.pvBuffer =
 0959                    inSecBuffer.unmanagedToken != null ? inSecBuffer.unmanagedToken.DangerousGetHandle() :
 0960                    inSecBuffer.token == null || inSecBuffer.token.Length == 0 ? IntPtr.Zero :
 0961                    (IntPtr)(pinnedInSecBufferToken + inSecBuffer.offset);
 962
 0963                Interop.SspiCli.CredHandle contextHandle = refContext != null ? refContext._handle : default;
 964
 0965                if (refContext == null || refContext.IsInvalid)
 0966                {
 967                    // Previous versions unconditionally built a new "refContext" here, but would pass
 968                    // incorrect arguments to ApplyControlToken in cases where a nonzero "contextHandle" was
 969                    // already present. In these cases, allow the "refContext" to flow through unmodified
 970                    // (which will generate an ObjectDisposedException below). In all other cases, continue to
 971                    // build a new "refContext" in an attempt to maximize compat.
 0972                    if (contextHandle.IsZero)
 0973                    {
 0974                        refContext = new SafeDeleteSslContext();
 0975                    }
 0976                }
 977
 0978                bool gotRef = false;
 979                try
 0980                {
 0981                    refContext!.DangerousAddRef(ref gotRef);
 0982                    errorCode = Interop.SspiCli.ApplyControlToken(contextHandle.IsZero ? null : &contextHandle, ref inSe
 0983                }
 984                finally
 0985                {
 0986                    if (gotRef)
 0987                    {
 0988                        refContext!.DangerousRelease();
 0989                    }
 0990                }
 0991            }
 992
 0993            return errorCode;
 0994        }
 995    }
 996
 997    internal sealed class SafeDeleteSslContext : SafeDeleteContext
 998    {
 999        public SafeDeleteSslContext() : base() { }
 1000
 1001        protected override bool ReleaseHandle()
 1002        {
 1003            this._EffectiveCredential?.DangerousRelease();
 1004            return Interop.SspiCli.DeleteSecurityContext(ref _handle) == 0;
 1005        }
 1006    }
 1007
 1008    // Based on SafeFreeContextBuffer.
 1009    internal abstract class SafeFreeContextBufferChannelBinding : ChannelBinding
 1010    {
 1011        private int _size;
 1012
 1013        public override int Size
 1014        {
 1015            get { return _size; }
 1016        }
 1017
 1018        public override bool IsInvalid
 1019        {
 1020            get { return handle == new IntPtr(0) || handle == new IntPtr(-1); }
 1021        }
 1022
 1023        internal void Set(IntPtr value)
 1024        {
 1025            this.handle = value;
 1026        }
 1027
 1028        internal static SafeFreeContextBufferChannelBinding CreateEmptyHandle()
 1029        {
 1030            return new SafeFreeContextBufferChannelBinding_SECURITY();
 1031        }
 1032
 1033        public static unsafe int QueryContextChannelBinding(SafeDeleteContext phContext, Interop.SspiCli.ContextAttribut
 1034        {
 1035            int status = (int)Interop.SECURITY_STATUS.InvalidHandle;
 1036
 1037            // SCHANNEL only supports SECPKG_ATTR_ENDPOINT_BINDINGS and SECPKG_ATTR_UNIQUE_BINDINGS which
 1038            // map to our enum ChannelBindingKind.Endpoint and ChannelBindingKind.Unique.
 1039            if (contextAttribute != Interop.SspiCli.ContextAttribute.SECPKG_ATTR_ENDPOINT_BINDINGS &&
 1040                contextAttribute != Interop.SspiCli.ContextAttribute.SECPKG_ATTR_UNIQUE_BINDINGS)
 1041            {
 1042                return status;
 1043            }
 1044
 1045            bool refAdded = false;
 1046            try
 1047            {
 1048                phContext.DangerousAddRef(ref refAdded);
 1049                status = Interop.SspiCli.QueryContextAttributesW(ref phContext._handle, contextAttribute, buffer);
 1050            }
 1051            finally
 1052            {
 1053                if (refAdded)
 1054                {
 1055                    phContext.DangerousRelease();
 1056                }
 1057            }
 1058
 1059            if (status == 0 && refHandle != null)
 1060            {
 1061                refHandle.Set((*buffer).Bindings);
 1062                refHandle._size = (*buffer).BindingsLength;
 1063            }
 1064
 1065            if (status != 0)
 1066            {
 1067                refHandle?.SetHandleAsInvalid();
 1068            }
 1069
 1070            return status;
 1071        }
 1072
 1073        public override string? ToString()
 1074        {
 1075            if (IsInvalid)
 1076            {
 1077                return null;
 1078            }
 1079
 1080            var bytes = new byte[_size];
 1081            Marshal.Copy(handle, bytes, 0, bytes.Length);
 1082            return BitConverter.ToString(bytes).Replace('-', ' ');
 1083        }
 1084    }
 1085
 1086    internal sealed class SafeFreeContextBufferChannelBinding_SECURITY : SafeFreeContextBufferChannelBinding
 1087    {
 1088        protected override bool ReleaseHandle()
 1089        {
 1090            return Interop.SspiCli.FreeContextBuffer(handle) == 0;
 1091        }
 1092    }
 1093}
 1094