v9.7.2018.0421   支持运行时修改DAL连接字符串
大石头 编写于 2018-04-21 14:00:47
X
using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Principal;

namespace NewLife.Net.Http
{
    /// <summary>NTLM验证</summary>
    [SuppressUnmanagedCodeSecurity]
    sealed class NtlmAuth : IDisposable
    {
        #region 属性
        private string _blob;
        private bool _completed;
        private SecHandle _credentialsHandle;
        private bool _credentialsHandleAcquired;
        private SecBuffer _inputBuffer;
        private SecBufferDesc _inputBufferDesc;
        private SecBuffer _outputBuffer;
        private SecBufferDesc _outputBufferDesc;
        private SecHandle _securityContext;
        private bool _securityContextAcquired;
        private uint _securityContextAttributes;
        private SecurityIdentifier _sid;
        private long _timestamp;
        private const int ISC_REQ_ALLOCATE_MEMORY = 0x100;
        private const int ISC_REQ_CONFIDENTIALITY = 0x10;
        private const int ISC_REQ_DELEGATE = 1;
        private const int ISC_REQ_MUTUAL_AUTH = 2;
        private const int ISC_REQ_PROMPT_FOR_CREDS = 0x40;
        private const int ISC_REQ_REPLAY_DETECT = 4;
        private const int ISC_REQ_SEQUENCE_DETECT = 8;
        private const int ISC_REQ_STANDARD_FLAGS = 20;
        private const int ISC_REQ_USE_SESSION_KEY = 0x20;
        private const int ISC_REQ_USE_SUPPLIED_CREDS = 0x80;
        private const int SEC_E_OK = 0;
        private const int SEC_I_COMPLETE_AND_CONTINUE = 0x90314;
        private const int SEC_I_COMPLETE_NEEDED = 0x90313;
        private const int SEC_I_CONTINUE_NEEDED = 0x90312;
        private const int SECBUFFER_DATA = 1;
        private const int SECBUFFER_EMPTY = 0;
        private const int SECBUFFER_TOKEN = 2;
        private const int SECBUFFER_VERSION = 0;
        private const int SECPKG_CRED_INBOUND = 1;
        private const int SECURITY_NETWORK_DREP = 0;

        public string Blob { get { return _blob; } }

        public bool Completed { get { return _completed; } }

        public SecurityIdentifier SID { get { return _sid; } }
        #endregion

        #region 构造
        public NtlmAuth()
        {
            if (AcquireCredentialsHandle(null, "NTLM", 1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ref _credentialsHandle, ref _timestamp) != 0) throw new InvalidOperationException();
            _credentialsHandleAcquired = true;
        }

        ~NtlmAuth() { FreeUnmanagedResources(); }

        [DllImport("SECUR32.DLL", CharSet = CharSet.Unicode)]
        private static extern int DeleteSecurityContext(ref SecHandle phContext);

        [DllImport("SECUR32.DLL", CharSet = CharSet.Unicode)]
        private static extern int FreeCredentialsHandle(ref SecHandle phCredential);
        private void FreeUnmanagedResources()
        {
            if (_securityContextAcquired) DeleteSecurityContext(ref _securityContext);
            if (_credentialsHandleAcquired) FreeCredentialsHandle(ref _credentialsHandle);
        }
        void IDisposable.Dispose()
        {
            FreeUnmanagedResources();
            GC.SuppressFinalize(this);
        }
        #endregion

        #region 方法
        public unsafe bool Authenticate(string blobString)
        {
            _blob = null;
            byte[] buffer = Convert.FromBase64String(blobString);
            byte[] inArray = new byte[0x4000];
            fixed (SecHandle* ptrRef = &_securityContext)
            {
                fixed (SecBuffer* ptrRef2 = &_inputBuffer)
                {
                    fixed (SecBuffer* ptrRef3 = &_outputBuffer)
                    {
                        fixed (Byte* ptrRef4 = buffer)
                        {
                            fixed (Byte* ptrRef5 = inArray)
                            {
                                IntPtr zero = IntPtr.Zero;
                                if (_securityContextAcquired) zero = (IntPtr)ptrRef;
                                _inputBufferDesc.ulVersion = 0;
                                _inputBufferDesc.cBuffers = 1;
                                _inputBufferDesc.pBuffers = (IntPtr)ptrRef2;
                                _inputBuffer.cbBuffer = (uint)buffer.Length;
                                _inputBuffer.BufferType = 2;
                                _inputBuffer.pvBuffer = (IntPtr)ptrRef4;
                                _outputBufferDesc.ulVersion = 0;
                                _outputBufferDesc.cBuffers = 1;
                                _outputBufferDesc.pBuffers = (IntPtr)ptrRef3;
                                _outputBuffer.cbBuffer = (uint)inArray.Length;
                                _outputBuffer.BufferType = 2;
                                _outputBuffer.pvBuffer = (IntPtr)ptrRef5;
                                int num = AcceptSecurityContext(ref _credentialsHandle, zero, ref _inputBufferDesc, 20, 0, ref _securityContext, ref _outputBufferDesc, ref _securityContextAttributes, ref _timestamp);
                                if (num == 0x90312)
                                {
                                    _securityContextAcquired = true;
                                    _blob = Convert.ToBase64String(inArray, 0, (int)_outputBuffer.cbBuffer);
                                }
                                else
                                {
                                    if (num != 0) return false;
                                    IntPtr phToken = IntPtr.Zero;
                                    if (QuerySecurityContextToken(ref _securityContext, ref phToken) != 0) return false;
                                    try
                                    {
                                        using (WindowsIdentity identity = new WindowsIdentity(phToken))
                                        {
                                            _sid = identity.User;
                                        }
                                    }
                                    finally
                                    {
                                        CloseHandle(phToken);
                                    }
                                    _completed = true;
                                }
                            }
                        }
                    }
                }
            }
            return true;
        }

        [DllImport("SECUR32.DLL", CharSet = CharSet.Unicode)]
        private static extern int AcceptSecurityContext(ref SecHandle phCredential, IntPtr phContext, ref SecBufferDesc pInput, uint fContextReq, uint TargetDataRep, ref SecHandle phNewContext, ref SecBufferDesc pOutput, ref uint pfContextAttr, ref long ptsTimeStamp);
        [DllImport("SECUR32.DLL", CharSet = CharSet.Unicode)]
        private static extern int AcquireCredentialsHandle(string pszPrincipal, string pszPackage, uint fCredentialUse, IntPtr pvLogonID, IntPtr pAuthData, IntPtr pGetKeyFn, IntPtr pvGetKeyArgument, ref SecHandle phCredential, ref long ptsExpiry);

        [DllImport("KERNEL32.DLL", CharSet = CharSet.Unicode)]
        private static extern int CloseHandle(IntPtr phToken);

        [DllImport("SECUR32.DLL", CharSet = CharSet.Unicode)]
        private static extern int QuerySecurityContextToken(ref SecHandle phContext, ref IntPtr phToken);
        #endregion

        #region 内部结构
        [StructLayout(LayoutKind.Sequential)]
        private struct SecBuffer
        {
            public uint cbBuffer;
            public uint BufferType;
            public IntPtr pvBuffer;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct SecBufferDesc
        {
            public uint ulVersion;
            public uint cBuffers;
            public IntPtr pBuffers;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct SecHandle
        {
            public IntPtr dwLower;
            public IntPtr dwUpper;
        }
        #endregion
    }
}