|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Net;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace System.DirectoryServices.Protocols
{
public delegate LdapConnection QueryForConnectionCallback(LdapConnection primaryConnection, LdapConnection referralFromConnection, string newDistinguishedName, LdapDirectoryIdentifier identifier, NetworkCredential credential, long currentUserToken);
public delegate bool NotifyOfNewConnectionCallback(LdapConnection primaryConnection, LdapConnection referralFromConnection, string newDistinguishedName, LdapDirectoryIdentifier identifier, LdapConnection newConnection, NetworkCredential credential, long currentUserToken, int errorCodeFromBind);
public delegate void DereferenceConnectionCallback(LdapConnection primaryConnection, LdapConnection connectionToDereference);
public delegate X509Certificate QueryClientCertificateCallback(LdapConnection connection, byte[][] trustedCAs);
public delegate bool VerifyServerCertificateCallback(LdapConnection connection, X509Certificate certificate);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal unsafe delegate int QUERYFORCONNECTIONInternal(IntPtr Connection, IntPtr ReferralFromConnection, IntPtr NewDNPtr, IntPtr HostName, int PortNumber, SEC_WINNT_AUTH_IDENTITY_EX.Native* SecAuthIdentity, Luid* CurrentUserToken, IntPtr* ConnectionToUse);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal unsafe delegate Interop.BOOL NOTIFYOFNEWCONNECTIONInternal(IntPtr Connection, IntPtr ReferralFromConnection, IntPtr NewDNPtr, IntPtr HostName, IntPtr NewConnection, int PortNumber, SEC_WINNT_AUTH_IDENTITY_EX.Native* SecAuthIdentity, Luid* CurrentUser, int ErrorCodeFromBind);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate int DEREFERENCECONNECTIONInternal(IntPtr Connection, IntPtr ConnectionToDereference);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Interop.BOOL VERIFYSERVERCERT(IntPtr Connection, IntPtr pServerCert);
[Flags]
public enum LocatorFlags : long
{
None = 0,
ForceRediscovery = 0x00000001,
DirectoryServicesRequired = 0x00000010,
DirectoryServicesPreferred = 0x00000020,
GCRequired = 0x00000040,
PdcRequired = 0x00000080,
IPRequired = 0x00000200,
KdcRequired = 0x00000400,
TimeServerRequired = 0x00000800,
WriteableRequired = 0x00001000,
GoodTimeServerPreferred = 0x00002000,
AvoidSelf = 0x00004000,
OnlyLdapNeeded = 0x00008000,
IsFlatName = 0x00010000,
IsDnsName = 0x00020000,
ReturnDnsName = 0x40000000,
ReturnFlatName = 0x80000000,
}
public enum SecurityProtocol
{
Pct1Server = 0x1,
Pct1Client = 0x2,
Ssl2Server = 0x4,
Ssl2Client = 0x8,
Ssl3Server = 0x10,
Ssl3Client = 0x20,
Tls1Server = 0x40,
Tls1Client = 0x80
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class SecurityPackageContextConnectionInformation
{
// Not marked as readonly to enable passing to Unsafe.As in GetPinnableReference.
private SecurityProtocol _securityProtocol;
#pragma warning disable SYSLIB0058 // Use NegotiatedCipherSuite.
private readonly CipherAlgorithmType _identifier;
private readonly int _strength;
private readonly HashAlgorithmType _hashAlgorithm;
#pragma warning restore SYSLIB0058 // Use NegotiatedCipherSuite.
private readonly int _hashStrength;
private readonly int _keyExchangeAlgorithm;
private readonly int _exchangeStrength;
internal SecurityPackageContextConnectionInformation()
{
}
public SecurityProtocol Protocol => _securityProtocol;
#if NET10_0_OR_GREATER
[Obsolete(Obsoletions.TlsCipherAlgorithmEnumsMessage, DiagnosticId = Obsoletions.TlsCipherAlgorithmEnumsDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
#endif
public CipherAlgorithmType AlgorithmIdentifier => _identifier;
#if NET10_0_OR_GREATER
[Obsolete(Obsoletions.TlsCipherAlgorithmEnumsMessage, DiagnosticId = Obsoletions.TlsCipherAlgorithmEnumsDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
#endif
public int CipherStrength => _strength;
#if NET10_0_OR_GREATER
[Obsolete(Obsoletions.TlsCipherAlgorithmEnumsMessage, DiagnosticId = Obsoletions.TlsCipherAlgorithmEnumsDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
#endif
public HashAlgorithmType Hash => _hashAlgorithm;
#if NET10_0_OR_GREATER
[Obsolete(Obsoletions.TlsCipherAlgorithmEnumsMessage, DiagnosticId = Obsoletions.TlsCipherAlgorithmEnumsDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
#endif
public int HashStrength => _hashStrength;
#if NET10_0_OR_GREATER
[Obsolete(Obsoletions.TlsCipherAlgorithmEnumsMessage, DiagnosticId = Obsoletions.TlsCipherAlgorithmEnumsDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
#endif
public int KeyExchangeAlgorithm => _keyExchangeAlgorithm;
#if NET10_0_OR_GREATER
[Obsolete(Obsoletions.TlsCipherAlgorithmEnumsMessage, DiagnosticId = Obsoletions.TlsCipherAlgorithmEnumsDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
#endif
public int ExchangeStrength => _exchangeStrength;
internal ref readonly byte GetPinnableReference() => ref Unsafe.As<SecurityProtocol, byte>(ref _securityProtocol);
}
public sealed class ReferralCallback
{
public ReferralCallback()
{
}
public QueryForConnectionCallback QueryForConnection { get; set; }
public NotifyOfNewConnectionCallback NotifyNewConnection { get; set; }
public DereferenceConnectionCallback DereferenceConnection { get; set; }
}
internal struct SecurityHandle
{
public IntPtr Lower;
public IntPtr Upper;
}
public partial class LdapSessionOptions
{
private readonly LdapConnection _connection;
private ReferralCallback _callbackRoutine = new ReferralCallback();
internal QueryClientCertificateCallback _clientCertificateDelegate;
private VerifyServerCertificateCallback _serverCertificateDelegate;
private readonly QUERYFORCONNECTIONInternal _queryDelegate;
private readonly NOTIFYOFNEWCONNECTIONInternal _notifiyDelegate;
private readonly DEREFERENCECONNECTIONInternal _dereferenceDelegate;
private readonly VERIFYSERVERCERT _serverCertificateRoutine;
internal unsafe LdapSessionOptions(LdapConnection connection)
{
_connection = connection;
_queryDelegate = new QUERYFORCONNECTIONInternal(ProcessQueryConnection);
_notifiyDelegate = new NOTIFYOFNEWCONNECTIONInternal(ProcessNotifyConnection);
_dereferenceDelegate = new DEREFERENCECONNECTIONInternal(ProcessDereferenceConnection);
_serverCertificateRoutine = new VERIFYSERVERCERT(ProcessServerCertificate);
}
public int ReferralHopLimit
{
get => GetIntValueHelper(LdapOption.LDAP_OPT_REFERRAL_HOP_LIMIT);
set
{
if (value < 0)
{
throw new ArgumentException(SR.ValidValue, nameof(value));
}
SetIntValueHelper(LdapOption.LDAP_OPT_REFERRAL_HOP_LIMIT, value);
}
}
public string HostName
{
get => GetStringValueHelper(LdapOption.LDAP_OPT_HOST_NAME, false);
set => SetStringValueHelper(LdapOption.LDAP_OPT_HOST_NAME, value);
}
public string DomainName
{
get => GetStringValueHelper(LdapOption.LDAP_OPT_DNSDOMAIN_NAME, true);
set => SetStringValueHelper(LdapOption.LDAP_OPT_DNSDOMAIN_NAME, value);
}
public LocatorFlags LocatorFlag
{
get
{
int result = GetIntValueHelper(LdapOption.LDAP_OPT_GETDSNAME_FLAGS);
return (LocatorFlags)result;
}
set
{
// We don't do validation to the dirsync flag here as underneath API does not check for it and we don't want to put
// unnecessary limitation on it.
SetIntValueHelper(LdapOption.LDAP_OPT_GETDSNAME_FLAGS, (int)value);
}
}
public bool HostReachable
{
get
{
int outValue = GetIntValueHelper(LdapOption.LDAP_OPT_HOST_REACHABLE);
return outValue == 1;
}
}
public TimeSpan PingKeepAliveTimeout
{
get
{
int result = GetIntValueHelper(LdapOption.LDAP_OPT_PING_KEEP_ALIVE);
return new TimeSpan(result * TimeSpan.TicksPerSecond);
}
set
{
if (value < TimeSpan.Zero)
{
throw new ArgumentException(SR.NoNegativeTimeLimit, nameof(value));
}
// Prevent integer overflow.
if (value.TotalSeconds > int.MaxValue)
{
throw new ArgumentException(SR.TimespanExceedMax, nameof(value));
}
int seconds = (int)(value.Ticks / TimeSpan.TicksPerSecond);
SetIntValueHelper(LdapOption.LDAP_OPT_PING_KEEP_ALIVE, seconds);
}
}
public int PingLimit
{
get => GetIntValueHelper(LdapOption.LDAP_OPT_PING_LIMIT);
set
{
if (value < 0)
{
throw new ArgumentException(SR.ValidValue, nameof(value));
}
SetIntValueHelper(LdapOption.LDAP_OPT_PING_LIMIT, value);
}
}
public TimeSpan PingWaitTimeout
{
get
{
int result = GetIntValueHelper(LdapOption.LDAP_OPT_PING_WAIT_TIME);
return new TimeSpan(result * TimeSpan.TicksPerMillisecond);
}
set
{
if (value < TimeSpan.Zero)
{
throw new ArgumentException(SR.NoNegativeTimeLimit, nameof(value));
}
// Prevent integer overflow.
if (value.TotalMilliseconds > int.MaxValue)
{
throw new ArgumentException(SR.TimespanExceedMax, nameof(value));
}
int milliseconds = (int)(value.Ticks / TimeSpan.TicksPerMillisecond);
SetIntValueHelper(LdapOption.LDAP_OPT_PING_WAIT_TIME, milliseconds);
}
}
public bool AutoReconnect
{
get
{
int outValue = GetIntValueHelper(LdapOption.LDAP_OPT_AUTO_RECONNECT);
return outValue == 1;
}
set
{
int temp = value ? 1 : 0;
SetIntValueHelper(LdapOption.LDAP_OPT_AUTO_RECONNECT, temp);
}
}
public int SspiFlag
{
get => GetIntValueHelper(LdapOption.LDAP_OPT_SSPI_FLAGS);
set => SetIntValueHelper(LdapOption.LDAP_OPT_SSPI_FLAGS, value);
}
public SecurityPackageContextConnectionInformation SslInformation
{
get
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
var secInfo = new SecurityPackageContextConnectionInformation();
int error = LdapPal.GetSecInfoOption(_connection._ldapHandle, LdapOption.LDAP_OPT_SSL_INFO, secInfo);
ErrorChecking.CheckAndSetLdapError(error);
return secInfo;
}
}
public object SecurityContext
{
get
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
SecurityHandle tempHandle = default;
int error = LdapPal.GetSecurityHandleOption(_connection._ldapHandle, LdapOption.LDAP_OPT_SECURITY_CONTEXT, ref tempHandle);
ErrorChecking.CheckAndSetLdapError(error);
return tempHandle;
}
}
public bool Signing
{
get
{
int outValue = GetIntValueHelper(LdapOption.LDAP_OPT_SIGN);
return outValue == 1;
}
set
{
int temp = value ? 1 : 0;
SetIntValueHelper(LdapOption.LDAP_OPT_SIGN, temp);
}
}
public bool Sealing
{
get
{
int outValue = GetIntValueHelper(LdapOption.LDAP_OPT_ENCRYPT);
return outValue == 1;
}
set
{
int temp = value ? 1 : 0;
SetIntValueHelper(LdapOption.LDAP_OPT_ENCRYPT, temp);
}
}
public string SaslMethod
{
get => GetStringValueHelper(LdapOption.LDAP_OPT_SASL_METHOD, true);
set => SetStringValueHelper(LdapOption.LDAP_OPT_SASL_METHOD, value);
}
public bool RootDseCache
{
get
{
int outValue = GetIntValueHelper(LdapOption.LDAP_OPT_ROOTDSE_CACHE);
return outValue == 1;
}
set
{
int temp = value ? 1 : 0;
SetIntValueHelper(LdapOption.LDAP_OPT_ROOTDSE_CACHE, temp);
}
}
public bool TcpKeepAlive
{
get
{
int outValue = GetIntValueHelper(LdapOption.LDAP_OPT_TCP_KEEPALIVE);
return outValue == 1;
}
set
{
int temp = value ? 1 : 0;
SetIntValueHelper(LdapOption.LDAP_OPT_TCP_KEEPALIVE, temp);
}
}
public TimeSpan SendTimeout
{
get
{
int result = GetIntValueHelper(LdapOption.LDAP_OPT_SEND_TIMEOUT);
return new TimeSpan(result * TimeSpan.TicksPerSecond);
}
set
{
if (value < TimeSpan.Zero)
{
throw new ArgumentException(SR.NoNegativeTimeLimit, nameof(value));
}
// Prevent integer overflow.
if (value.TotalSeconds > int.MaxValue)
{
throw new ArgumentException(SR.TimespanExceedMax, nameof(value));
}
int seconds = (int)(value.Ticks / TimeSpan.TicksPerSecond);
SetIntValueHelper(LdapOption.LDAP_OPT_SEND_TIMEOUT, seconds);
}
}
public ReferralCallback ReferralCallback
{
get
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
return _callbackRoutine;
}
set
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
var tempCallback = new ReferralCallback();
if (value != null)
{
tempCallback.QueryForConnection = value.QueryForConnection;
tempCallback.NotifyNewConnection = value.NotifyNewConnection;
tempCallback.DereferenceConnection = value.DereferenceConnection;
}
else
{
tempCallback.QueryForConnection = null;
tempCallback.NotifyNewConnection = null;
tempCallback.DereferenceConnection = null;
}
ProcessCallBackRoutine(tempCallback);
_callbackRoutine = value;
}
}
public QueryClientCertificateCallback QueryClientCertificate
{
get
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
return _clientCertificateDelegate;
}
set
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
if (value != null)
{
int certError = LdapPal.SetClientCertOption(_connection._ldapHandle, LdapOption.LDAP_OPT_CLIENT_CERTIFICATE, _connection._clientCertificateRoutine);
if (certError != (int)ResultCode.Success)
{
if (LdapErrorMappings.IsLdapError(certError))
{
string certerrorMessage = LdapErrorMappings.MapResultCode(certError);
throw new LdapException(certError, certerrorMessage);
}
else
{
throw new LdapException(certError);
}
}
// A certificate callback is specified so automatic bind is disabled.
_connection.AutoBind = false;
}
_clientCertificateDelegate = value;
}
}
public VerifyServerCertificateCallback VerifyServerCertificate
{
get
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
return _serverCertificateDelegate;
}
set
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
if (value != null)
{
int error = LdapPal.SetServerCertOption(_connection._ldapHandle, LdapOption.LDAP_OPT_SERVER_CERTIFICATE, _serverCertificateRoutine);
ErrorChecking.CheckAndSetLdapError(error);
}
_serverCertificateDelegate = value;
}
}
internal DereferenceAlias DerefAlias
{
get => (DereferenceAlias)GetIntValueHelper(LdapOption.LDAP_OPT_DEREF);
set => SetIntValueHelper(LdapOption.LDAP_OPT_DEREF, (int)value);
}
internal void SetFqdnRequired()
{
SetIntValueHelper(LdapOption.LDAP_OPT_AREC_EXCLUSIVE, 1);
}
public void FastConcurrentBind()
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
// Bump up the protocol version
ProtocolVersion = 3;
// Do the fast concurrent bind.
int inValue = 1;
int error = LdapPal.SetIntOption(_connection._ldapHandle, LdapOption.LDAP_OPT_FAST_CONCURRENT_BIND, ref inValue);
ErrorChecking.CheckAndSetLdapError(error);
}
public unsafe void StartTransportLayerSecurity(DirectoryControlCollection controls)
{
IntPtr serverControlArray = IntPtr.Zero;
LdapControl[] managedServerControls = null;
IntPtr clientControlArray = IntPtr.Zero;
LdapControl[] managedClientControls = null;
IntPtr ldapResult = IntPtr.Zero;
IntPtr referral = IntPtr.Zero;
int serverError = 0;
Uri[] responseReferral = null;
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
try
{
// build server control
managedServerControls = LdapConnection.BuildControlArray(controls, true);
int structSize = Marshal.SizeOf<LdapControl>();
if (managedServerControls != null)
{
serverControlArray = Utility.AllocHGlobalIntPtrArray(managedServerControls.Length + 1);
void** pServerControlArray = (void**)serverControlArray;
for (int i = 0; i < managedServerControls.Length; i++)
{
IntPtr controlPtr = Marshal.AllocHGlobal(structSize);
Marshal.StructureToPtr(managedServerControls[i], controlPtr, false);
pServerControlArray[i] = (void*)controlPtr;
}
pServerControlArray[managedServerControls.Length] = null;
}
// Build client control.
managedClientControls = LdapConnection.BuildControlArray(controls, false);
if (managedClientControls != null)
{
clientControlArray = Utility.AllocHGlobalIntPtrArray(managedClientControls.Length + 1);
void** pClientControlArray = (void**)clientControlArray;
for (int i = 0; i < managedClientControls.Length; i++)
{
IntPtr controlPtr = Marshal.AllocHGlobal(structSize);
Marshal.StructureToPtr(managedClientControls[i], controlPtr, false);
pClientControlArray[i] = (void*)controlPtr;
}
pClientControlArray[managedClientControls.Length] = null;
}
int error = LdapPal.StartTls(_connection._ldapHandle, ref serverError, ref ldapResult, serverControlArray, clientControlArray);
if (ldapResult != IntPtr.Zero)
{
// Parse the referral.
int resultError = LdapPal.ParseResultReferral(_connection._ldapHandle, ldapResult, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ref referral, IntPtr.Zero, 0 /* not free it */);
if (resultError == 0 && referral != IntPtr.Zero)
{
char** referralPtr = (char**)referral;
char* singleReferral = referralPtr[0];
int i = 0;
ArrayList referralList = new ArrayList();
while (singleReferral != null)
{
string s = LdapPal.PtrToString((IntPtr)singleReferral);
referralList.Add(s);
i++;
singleReferral = referralPtr[i];
}
// Free heap memory.
if (referral != IntPtr.Zero)
{
LdapPal.FreeValue(referral);
referral = IntPtr.Zero;
}
if (referralList.Count > 0)
{
responseReferral = new Uri[referralList.Count];
for (int j = 0; j < referralList.Count; j++)
{
responseReferral[j] = new Uri((string)referralList[j]);
}
}
}
}
if (error != (int)ResultCode.Success)
{
if (Utility.IsResultCode((ResultCode)error))
{
// If the server failed request for whatever reason, the ldap_start_tls returns LDAP_OTHER
// and the ServerReturnValue will contain the error code from the server.
if (error == (int)ResultCode.Other)
{
error = serverError;
}
string errorMessage = OperationErrorMappings.MapResultCode(error);
ExtendedResponse response = new ExtendedResponse(null, null, (ResultCode)error, errorMessage, responseReferral);
response.ResponseName = "1.3.6.1.4.1.1466.20037";
throw new TlsOperationException(response);
}
if (LdapErrorMappings.IsLdapError(error))
{
string errorMessage = LdapErrorMappings.MapResultCode(error);
throw new LdapException(error, errorMessage);
}
throw new LdapException(error);
}
}
finally
{
if (serverControlArray != IntPtr.Zero)
{
// Release the memory from the heap.
for (int i = 0; i < managedServerControls.Length; i++)
{
IntPtr tempPtr = Marshal.ReadIntPtr(serverControlArray, IntPtr.Size * i);
if (tempPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(tempPtr);
}
}
Marshal.FreeHGlobal(serverControlArray);
}
if (managedServerControls != null)
{
for (int i = 0; i < managedServerControls.Length; i++)
{
if (managedServerControls[i].ldctl_oid != IntPtr.Zero)
{
Marshal.FreeHGlobal(managedServerControls[i].ldctl_oid);
}
if (managedServerControls[i].ldctl_value != null)
{
if (managedServerControls[i].ldctl_value.bv_val != IntPtr.Zero)
{
Marshal.FreeHGlobal(managedServerControls[i].ldctl_value.bv_val);
}
}
}
}
if (clientControlArray != IntPtr.Zero)
{
// Release the memor from the heap.
for (int i = 0; i < managedClientControls.Length; i++)
{
IntPtr tempPtr = Marshal.ReadIntPtr(clientControlArray, IntPtr.Size * i);
if (tempPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(tempPtr);
}
}
Marshal.FreeHGlobal(clientControlArray);
}
if (managedClientControls != null)
{
for (int i = 0; i < managedClientControls.Length; i++)
{
if (managedClientControls[i].ldctl_oid != IntPtr.Zero)
{
Marshal.FreeHGlobal(managedClientControls[i].ldctl_oid);
}
if (managedClientControls[i].ldctl_value != null)
{
if (managedClientControls[i].ldctl_value.bv_val != IntPtr.Zero)
Marshal.FreeHGlobal(managedClientControls[i].ldctl_value.bv_val);
}
}
}
if (referral != IntPtr.Zero)
{
LdapPal.FreeValue(referral);
}
}
}
public void StopTransportLayerSecurity()
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
byte result = LdapPal.StopTls(_connection._ldapHandle);
if (result == 0)
{
throw new TlsOperationException(null, SR.TLSStopFailure);
}
}
public int ProtocolVersion
{
get => GetIntValueHelper(LdapOption.LDAP_OPT_VERSION);
set => SetIntValueHelper(LdapOption.LDAP_OPT_VERSION, value);
}
private int GetIntValueHelper(LdapOption option)
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
int outValue = 0;
int error = LdapPal.GetIntOption(_connection._ldapHandle, option, ref outValue);
ErrorChecking.CheckAndSetLdapError(error);
return outValue;
}
private void SetIntValueHelper(LdapOption option, int value)
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
int temp = value;
int error = LdapPal.SetIntOption(_connection._ldapHandle, option, ref temp);
ErrorChecking.CheckAndSetLdapError(error);
}
private IntPtr GetPtrValueHelper(LdapOption option)
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
IntPtr outValue = new IntPtr(0);
int error = LdapPal.GetPtrOption(_connection._ldapHandle, option, ref outValue);
ErrorChecking.CheckAndSetLdapError(error);
return outValue;
}
private void SetPtrValueHelper(LdapOption option, IntPtr value)
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
IntPtr temp = value;
int error = LdapPal.SetPtrOption(_connection._ldapHandle, option, ref temp);
ErrorChecking.CheckAndSetLdapError(error);
}
private string GetStringValueHelper(LdapOption option, bool releasePtr)
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
IntPtr outValue = new IntPtr(0);
int error = LdapPal.GetPtrOption(_connection._ldapHandle, option, ref outValue);
ErrorChecking.CheckAndSetLdapError(error);
string stringValue = null;
if (outValue != IntPtr.Zero)
{
stringValue = LdapPal.PtrToString(outValue);
}
if (releasePtr)
{
LdapPal.FreeMemory(outValue);
}
return stringValue;
}
private void SetStringValueHelper(LdapOption option, string value)
{
if (_connection._disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
IntPtr inValue = IntPtr.Zero;
if (value != null)
{
inValue = LdapPal.StringToPtr(value);
}
try
{
int error = LdapPal.SetPtrOption(_connection._ldapHandle, option, ref inValue);
ErrorChecking.CheckAndSetLdapError(error);
}
finally
{
if (inValue != IntPtr.Zero)
{
Marshal.FreeHGlobal(inValue);
}
}
}
private void ProcessCallBackRoutine(ReferralCallback tempCallback)
{
LdapReferralCallback value = new LdapReferralCallback()
{
sizeofcallback = Marshal.SizeOf<LdapReferralCallback>(),
query = tempCallback.QueryForConnection == null ? null : _queryDelegate,
notify = tempCallback.NotifyNewConnection == null ? null : _notifiyDelegate,
dereference = tempCallback.DereferenceConnection == null ? null : _dereferenceDelegate
};
int error = LdapPal.SetReferralOption(_connection._ldapHandle, LdapOption.LDAP_OPT_REFERRAL_CALLBACK, ref value);
ErrorChecking.CheckAndSetLdapError(error);
}
private unsafe int ProcessQueryConnection(IntPtr PrimaryConnection, IntPtr ReferralFromConnection, IntPtr NewDNPtr, IntPtr HostNamePtr, int PortNumber, SEC_WINNT_AUTH_IDENTITY_EX.Native* SecAuthIdentity, Luid* CurrentUserToken, IntPtr* ConnectionToUse)
{
*ConnectionToUse = IntPtr.Zero;
string NewDN = null;
// The user must have registered callback function.
Debug.Assert(_callbackRoutine.QueryForConnection != null);
// The user registers the QUERYFORCONNECTION callback.
if (_callbackRoutine.QueryForConnection != null)
{
if (NewDNPtr != IntPtr.Zero)
{
NewDN = LdapPal.PtrToString(NewDNPtr);
}
string target = $"{Marshal.PtrToStringUni(HostNamePtr)}:{PortNumber}";
var identifier = new LdapDirectoryIdentifier(target);
NetworkCredential cred = ProcessSecAuthIdentity(SecAuthIdentity);
LdapConnection tempReferralConnection = null;
// If ReferralFromConnection handle is valid.
if (ReferralFromConnection != IntPtr.Zero)
{
lock (LdapConnection.s_objectLock)
{
// Make sure first whether we have saved it in the handle table before
WeakReference reference = (WeakReference)(LdapConnection.s_handleTable[ReferralFromConnection]);
if (reference != null && reference.IsAlive)
{
// Save this before and object has not been garbage collected yet.
tempReferralConnection = (LdapConnection)reference.Target;
}
else
{
if (reference != null)
{
// Connection has been garbage collected, we need to remove this one.
LdapConnection.s_handleTable.Remove(ReferralFromConnection);
}
// We don't have it yet, construct a new one.
tempReferralConnection = new LdapConnection(((LdapDirectoryIdentifier)(_connection.Directory)), _connection.GetCredential(), _connection.AuthType, ReferralFromConnection);
// Save it to the handle table.
LdapConnection.s_handleTable.Add(ReferralFromConnection, new WeakReference(tempReferralConnection));
}
}
}
long tokenValue = (uint)CurrentUserToken->LowPart + (((long)CurrentUserToken->HighPart) << 32);
LdapConnection con = _callbackRoutine.QueryForConnection(_connection, tempReferralConnection, NewDN, identifier, cred, tokenValue);
if (con != null && con._ldapHandle != null && !con._ldapHandle.IsInvalid)
{
bool success = AddLdapHandleRef(con);
if (success)
{
*ConnectionToUse = con._ldapHandle.DangerousGetHandle();
}
}
return 0;
}
// The user does not take ownership of the connection.
return 1;
}
private unsafe Interop.BOOL ProcessNotifyConnection(IntPtr primaryConnection, IntPtr referralFromConnection, IntPtr newDNPtr, IntPtr hostNamePtr, IntPtr newConnection, int portNumber, SEC_WINNT_AUTH_IDENTITY_EX.Native* SecAuthIdentity, Luid* currentUser, int errorCodeFromBind)
{
if (newConnection != IntPtr.Zero && _callbackRoutine.NotifyNewConnection != null)
{
string newDN = null;
if (newDNPtr != IntPtr.Zero)
{
newDN = LdapPal.PtrToString(newDNPtr);
}
string target = $"{Marshal.PtrToStringUni(hostNamePtr)}:{portNumber}";
var identifier = new LdapDirectoryIdentifier(target);
NetworkCredential cred = ProcessSecAuthIdentity(SecAuthIdentity);
LdapConnection tempNewConnection = null;
LdapConnection tempReferralConnection = null;
lock (LdapConnection.s_objectLock)
{
// Check whether the referralFromConnection handle is valid.
if (referralFromConnection != IntPtr.Zero)
{
// Check whether we have save it in the handle table before.
WeakReference reference = (WeakReference)(LdapConnection.s_handleTable[referralFromConnection]);
if (reference != null && reference.Target is LdapConnection conn && conn._ldapHandle != null)
{
// Save this before and object has not been garbage collected yet.
tempReferralConnection = conn;
}
else
{
// Connection has been garbage collected, we need to remove this one.
if (reference != null)
{
LdapConnection.s_handleTable.Remove(referralFromConnection);
}
// We don't have it yet, construct a new one.
tempReferralConnection = new LdapConnection(((LdapDirectoryIdentifier)(_connection.Directory)), _connection.GetCredential(), _connection.AuthType, referralFromConnection);
// Save it to the handle table.
LdapConnection.s_handleTable.Add(referralFromConnection, new WeakReference(tempReferralConnection));
}
}
if (newConnection != IntPtr.Zero)
{
// Check whether we have save it in the handle table before.
WeakReference reference = (WeakReference)(LdapConnection.s_handleTable[newConnection]);
if (reference != null && reference.IsAlive && null != ((LdapConnection)reference.Target)._ldapHandle)
{
// Save this before and object has not been garbage collected yet.
tempNewConnection = (LdapConnection)reference.Target;
}
else
{
// Connection has been garbage collected, we need to remove this one.
if (reference != null)
{
LdapConnection.s_handleTable.Remove(newConnection);
}
// We don't have it yet, construct a new one.
tempNewConnection = new LdapConnection(identifier, cred, _connection.AuthType, newConnection);
// Save it to the handle table.
LdapConnection.s_handleTable.Add(newConnection, new WeakReference(tempNewConnection));
}
}
}
long tokenValue = (uint)currentUser->LowPart + (((long)currentUser->HighPart) << 32);
bool value = _callbackRoutine.NotifyNewConnection(_connection, tempReferralConnection, newDN, identifier, tempNewConnection, cred, tokenValue, errorCodeFromBind);
if (value)
{
value = AddLdapHandleRef(tempNewConnection);
if (value)
{
tempNewConnection.NeedDispose = true;
}
}
return value ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
}
return Interop.BOOL.FALSE;
}
private int ProcessDereferenceConnection(IntPtr PrimaryConnection, IntPtr ConnectionToDereference)
{
if (ConnectionToDereference != IntPtr.Zero && _callbackRoutine.DereferenceConnection != null)
{
LdapConnection dereferenceConnection;
WeakReference reference = null;
// in most cases it should be in our table
lock (LdapConnection.s_objectLock)
{
reference = (WeakReference)(LdapConnection.s_handleTable[ConnectionToDereference]);
}
// not been added to the table before or it is garbage collected, we need to construct it
if (reference == null || !reference.IsAlive)
{
dereferenceConnection = new LdapConnection(((LdapDirectoryIdentifier)(_connection.Directory)), _connection.GetCredential(), _connection.AuthType, ConnectionToDereference);
}
else
{
dereferenceConnection = (LdapConnection)reference.Target;
ReleaseLdapHandleRef(dereferenceConnection);
}
_callbackRoutine.DereferenceConnection(_connection, dereferenceConnection);
// there is no need to remove the connection object from the handle table, as it will be removed automatically when
// connection object is disposed.
}
return 1;
}
private unsafe NetworkCredential ProcessSecAuthIdentity(SEC_WINNT_AUTH_IDENTITY_EX.Native* SecAuthIdentit)
{
if (SecAuthIdentit == null)
{
return new NetworkCredential();
}
string user = Marshal.PtrToStringUni(SecAuthIdentit->user);
string domain = Marshal.PtrToStringUni(SecAuthIdentit->domain);
string password = Marshal.PtrToStringUni(SecAuthIdentit->password);
return new NetworkCredential(user, password, domain);
}
private Interop.BOOL ProcessServerCertificate(IntPtr connection, IntPtr serverCert)
{
// If callback is not specified by user, it means the server certificate is accepted.
bool value = true;
if (_serverCertificateDelegate != null)
{
IntPtr certPtr = IntPtr.Zero;
X509Certificate certificate = null;
try
{
Debug.Assert(serverCert != IntPtr.Zero);
certPtr = Marshal.ReadIntPtr(serverCert);
certificate = new X509Certificate(certPtr);
}
finally
{
PALCertFreeCRLContext(certPtr);
}
value = _serverCertificateDelegate(_connection, certificate);
}
return value ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
}
private static bool AddLdapHandleRef(LdapConnection ldapConnection)
{
bool success = false;
if (ldapConnection != null && ldapConnection._ldapHandle != null && !ldapConnection._ldapHandle.IsInvalid)
{
ldapConnection._ldapHandle.DangerousAddRef(ref success);
}
return success;
}
private static void ReleaseLdapHandleRef(LdapConnection ldapConnection)
{
if (ldapConnection != null && ldapConnection._ldapHandle != null && !ldapConnection._ldapHandle.IsInvalid)
{
ldapConnection._ldapHandle.DangerousRelease();
}
}
}
}
|