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 // See the LICENSE file in the project root for more information.
4 
5 using System.Net;
6 using System.Security.Principal;
7 using System.Diagnostics;
8 using System.Runtime.InteropServices;
9 using System.ComponentModel;
10 using System.Security.Permissions;
11 using System.IO;
12 
13 namespace System.DirectoryServices.ActiveDirectory
14 {
15     public enum DirectoryContextType
16     {
17         Domain = 0,
18         Forest = 1,
19         DirectoryServer = 2,
20         ConfigurationSet = 3,
21         ApplicationPartition = 4
22     }
23 
24     public class DirectoryContext
25     {
26         private string _name = null;
27         private DirectoryContextType _contextType;
28         private NetworkCredential _credential = null;
29         internal string serverName = null;
30         internal bool usernameIsNull = false;
31         internal bool passwordIsNull = false;
32         private bool _validated = false;
33         private bool _contextIsValid = false;
34 
35         internal static LoadLibrarySafeHandle ADHandle;
36         internal static LoadLibrarySafeHandle ADAMHandle;
37 
38         #region constructors
39 
DirectoryContext()40         static DirectoryContext()
41         {
42             // load ntdsapi.dll for AD and ADAM
43             GetLibraryHandle();
44         }
45 
46         // Internal Constructors
InitializeDirectoryContext(DirectoryContextType contextType, string name, string username, string password)47         internal void InitializeDirectoryContext(DirectoryContextType contextType, string name, string username, string password)
48         {
49             _name = name;
50             _contextType = contextType;
51             _credential = new NetworkCredential(username, password);
52             if (username == null)
53             {
54                 usernameIsNull = true;
55             }
56             if (password == null)
57             {
58                 passwordIsNull = true;
59             }
60         }
61 
DirectoryContext(DirectoryContextType contextType, string name, DirectoryContext context)62         internal DirectoryContext(DirectoryContextType contextType, string name, DirectoryContext context)
63         {
64             _name = name;
65             _contextType = contextType;
66 
67             if (context != null)
68             {
69                 _credential = context.Credential;
70                 this.usernameIsNull = context.usernameIsNull;
71                 this.passwordIsNull = context.passwordIsNull;
72             }
73             else
74             {
75                 _credential = new NetworkCredential(null, "", null);
76                 this.usernameIsNull = true;
77                 this.passwordIsNull = true;
78             }
79         }
80 
DirectoryContext(DirectoryContext context)81         internal DirectoryContext(DirectoryContext context)
82         {
83             _name = context.Name;
84             _contextType = context.ContextType;
85             _credential = context.Credential;
86             this.usernameIsNull = context.usernameIsNull;
87             this.passwordIsNull = context.passwordIsNull;
88             if (context.ContextType != DirectoryContextType.ConfigurationSet)
89             {
90                 //
91                 // only for configurationset, we select a server, so we should not copy over that
92                 // information, for all other types, this is either the same as name of the target or if the target is netbios name
93                 // (for domain and forest) it could be the dns name. We should copy over this information.
94                 //
95                 this.serverName = context.serverName;
96             }
97         }
98         #endregion constructors
99 
100         #region public constructors
101 
DirectoryContext(DirectoryContextType contextType)102         public DirectoryContext(DirectoryContextType contextType)
103         {
104             //
105             // this constructor can only be called for DirectoryContextType.Forest or DirectoryContextType.Domain
106             // since all other types require the name to be specified
107             //
108             if (contextType != DirectoryContextType.Domain && contextType != DirectoryContextType.Forest)
109             {
110                 throw new ArgumentException(SR.OnlyDomainOrForest, "contextType");
111             }
112 
113             InitializeDirectoryContext(contextType, null, null, null);
114         }
115 
DirectoryContext(DirectoryContextType contextType, string name)116         public DirectoryContext(DirectoryContextType contextType, string name)
117         {
118             if (contextType < DirectoryContextType.Domain || contextType > DirectoryContextType.ApplicationPartition)
119             {
120                 throw new InvalidEnumArgumentException("contextType", (int)contextType, typeof(DirectoryContextType));
121             }
122 
123             if (name == null)
124             {
125                 throw new ArgumentNullException("name");
126             }
127 
128             if (name.Length == 0)
129             {
130                 throw new ArgumentException(SR.EmptyStringParameter, "name");
131             }
132 
133             InitializeDirectoryContext(contextType, name, null, null);
134         }
135 
DirectoryContext(DirectoryContextType contextType, string username, string password)136         public DirectoryContext(DirectoryContextType contextType, string username, string password)
137         {
138             //
139             // this constructor can only be called for DirectoryContextType.Forest or DirectoryContextType.Domain
140             // since all other types require the name to be specified
141             //
142             if (contextType != DirectoryContextType.Domain && contextType != DirectoryContextType.Forest)
143             {
144                 throw new ArgumentException(SR.OnlyDomainOrForest, "contextType");
145             }
146 
147             InitializeDirectoryContext(contextType, null, username, password);
148         }
149 
DirectoryContext(DirectoryContextType contextType, string name, string username, string password)150         public DirectoryContext(DirectoryContextType contextType, string name, string username, string password)
151         {
152             if (contextType < DirectoryContextType.Domain || contextType > DirectoryContextType.ApplicationPartition)
153             {
154                 throw new InvalidEnumArgumentException("contextType", (int)contextType, typeof(DirectoryContextType));
155             }
156 
157             if (name == null)
158             {
159                 throw new ArgumentNullException("name");
160             }
161 
162             if (name.Length == 0)
163             {
164                 throw new ArgumentException(SR.EmptyStringParameter, "name");
165             }
166 
167             InitializeDirectoryContext(contextType, name, username, password);
168         }
169 
170         #endregion public methods
171 
172         #region public properties
173 
174         public string Name => _name;
175 
176         public string UserName => usernameIsNull ? null : _credential.UserName;
177 
178         internal string Password
179         {
180             get => passwordIsNull ? null : _credential.Password;
181         }
182 
183         public DirectoryContextType ContextType => _contextType;
184 
185         internal NetworkCredential Credential => _credential;
186 
187         #endregion public properties
188 
189         #region private methods
IsContextValid(DirectoryContext context, DirectoryContextType contextType)190         internal static bool IsContextValid(DirectoryContext context, DirectoryContextType contextType)
191         {
192             bool contextIsValid = false;
193 
194             if ((contextType == DirectoryContextType.Domain) || ((contextType == DirectoryContextType.Forest) && (context.Name == null)))
195             {
196                 string tmpTarget = context.Name;
197 
198                 if (tmpTarget == null)
199                 {
200                     // GetLoggedOnDomain returns the dns name of the logged on user's domain
201                     context.serverName = GetLoggedOnDomain();
202                     contextIsValid = true;
203                 }
204                 else
205                 {
206                     // check for domain
207                     int errorCode = 0;
208                     DomainControllerInfo domainControllerInfo;
209                     errorCode = Locator.DsGetDcNameWrapper(null, tmpTarget, null, (long)PrivateLocatorFlags.DirectoryServicesRequired, out domainControllerInfo);
210 
211                     if (errorCode == NativeMethods.ERROR_NO_SUCH_DOMAIN)
212                     {
213                         // try with force rediscovery
214 
215                         errorCode = Locator.DsGetDcNameWrapper(null, tmpTarget, null, (long)PrivateLocatorFlags.DirectoryServicesRequired | (long)LocatorOptions.ForceRediscovery, out domainControllerInfo);
216 
217                         if (errorCode == NativeMethods.ERROR_NO_SUCH_DOMAIN)
218                         {
219                             contextIsValid = false;
220                         }
221                         else if (errorCode != 0)
222                         {
223                             throw ExceptionHelper.GetExceptionFromErrorCode(errorCode);
224                         }
225                         else
226                         {
227                             Debug.Assert(domainControllerInfo != null);
228                             Debug.Assert(domainControllerInfo.DomainName != null);
229                             context.serverName = domainControllerInfo.DomainName;
230                             contextIsValid = true;
231                         }
232                     }
233                     else if (errorCode == NativeMethods.ERROR_INVALID_DOMAIN_NAME_FORMAT)
234                     {
235                         // we can get this error if the target it server:port (not a valid domain)
236                         contextIsValid = false;
237                     }
238                     else if (errorCode != 0)
239                     {
240                         throw ExceptionHelper.GetExceptionFromErrorCode(errorCode);
241                     }
242                     else
243                     {
244                         Debug.Assert(domainControllerInfo != null);
245                         Debug.Assert(domainControllerInfo.DomainName != null);
246                         context.serverName = domainControllerInfo.DomainName;
247                         contextIsValid = true;
248                     }
249                 }
250             }
251             else if (contextType == DirectoryContextType.Forest)
252             {
253                 Debug.Assert(context.Name != null);
254 
255                 // check for forest
256                 int errorCode = 0;
257                 DomainControllerInfo domainControllerInfo;
258                 errorCode = Locator.DsGetDcNameWrapper(null, context.Name, null, (long)(PrivateLocatorFlags.GCRequired | PrivateLocatorFlags.DirectoryServicesRequired), out domainControllerInfo);
259 
260                 if (errorCode == NativeMethods.ERROR_NO_SUCH_DOMAIN)
261                 {
262                     // try with force rediscovery
263 
264                     errorCode = Locator.DsGetDcNameWrapper(null, context.Name, null, (long)((PrivateLocatorFlags.GCRequired | PrivateLocatorFlags.DirectoryServicesRequired)) | (long)LocatorOptions.ForceRediscovery, out domainControllerInfo);
265 
266                     if (errorCode == NativeMethods.ERROR_NO_SUCH_DOMAIN)
267                     {
268                         contextIsValid = false;
269                     }
270                     else if (errorCode != 0)
271                     {
272                         throw ExceptionHelper.GetExceptionFromErrorCode(errorCode);
273                     }
274                     else
275                     {
276                         Debug.Assert(domainControllerInfo != null);
277                         Debug.Assert(domainControllerInfo.DnsForestName != null);
278                         context.serverName = domainControllerInfo.DnsForestName;
279                         contextIsValid = true;
280                     }
281                 }
282                 else if (errorCode == NativeMethods.ERROR_INVALID_DOMAIN_NAME_FORMAT)
283                 {
284                     // we can get this error if the target it server:port (not a valid forest)
285                     contextIsValid = false;
286                 }
287                 else if (errorCode != 0)
288                 {
289                     throw ExceptionHelper.GetExceptionFromErrorCode(errorCode);
290                 }
291                 else
292                 {
293                     Debug.Assert(domainControllerInfo != null);
294                     Debug.Assert(domainControllerInfo.DnsForestName != null);
295                     context.serverName = domainControllerInfo.DnsForestName;
296                     contextIsValid = true;
297                 }
298             }
299             else if (contextType == DirectoryContextType.ApplicationPartition)
300             {
301                 Debug.Assert(context.Name != null);
302 
303                 // check for application partition
304                 int errorCode = 0;
305                 DomainControllerInfo domainControllerInfo;
306                 errorCode = Locator.DsGetDcNameWrapper(null, context.Name, null, (long)PrivateLocatorFlags.OnlyLDAPNeeded, out domainControllerInfo);
307 
308                 if (errorCode == NativeMethods.ERROR_NO_SUCH_DOMAIN)
309                 {
310                     // try with force rediscovery
311 
312                     errorCode = Locator.DsGetDcNameWrapper(null, context.Name, null, (long)PrivateLocatorFlags.OnlyLDAPNeeded | (long)LocatorOptions.ForceRediscovery, out domainControllerInfo);
313 
314                     if (errorCode == NativeMethods.ERROR_NO_SUCH_DOMAIN)
315                     {
316                         contextIsValid = false;
317                     }
318                     else if (errorCode != 0)
319                     {
320                         throw ExceptionHelper.GetExceptionFromErrorCode(errorCode);
321                     }
322                     else
323                     {
324                         contextIsValid = true;
325                     }
326                 }
327                 else if (errorCode == NativeMethods.ERROR_INVALID_DOMAIN_NAME_FORMAT)
328                 {
329                     // we can get this error if the target it server:port (not a valid application partition)
330                     contextIsValid = false;
331                 }
332                 else if (errorCode != 0)
333                 {
334                     throw ExceptionHelper.GetExceptionFromErrorCode(errorCode);
335                 }
336                 else
337                 {
338                     contextIsValid = true;
339                 }
340             }
341             else if (contextType == DirectoryContextType.DirectoryServer)
342             {
343                 //
344                 // if the servername contains a port number, then remove that
345                 //
346                 string tempServerName = null;
347                 string portNumber;
348                 tempServerName = Utils.SplitServerNameAndPortNumber(context.Name, out portNumber);
349 
350                 //
351                 // this will validate that the name specified in the context is truely the name of a machine (and not of a domain)
352                 //
353                 DirectoryEntry de = new DirectoryEntry("WinNT://" + tempServerName + ",computer", context.UserName, context.Password, Utils.DefaultAuthType);
354                 try
355                 {
356                     de.Bind(true);
357                     contextIsValid = true;
358                 }
359                 catch (COMException e)
360                 {
361                     if ((e.ErrorCode == unchecked((int)0x80070035)) || (e.ErrorCode == unchecked((int)0x80070033)) || (e.ErrorCode == unchecked((int)0x80005000)))
362                     {
363                         // if this returns bad network path
364                         contextIsValid = false;
365                     }
366                     else
367                     {
368                         throw ExceptionHelper.GetExceptionFromCOMException(context, e);
369                     }
370                 }
371                 finally
372                 {
373                     de.Dispose();
374                 }
375             }
376             else
377             {
378                 // no special validation for ConfigurationSet
379                 contextIsValid = true;
380             }
381 
382             return contextIsValid;
383         }
384 
isRootDomain()385         internal bool isRootDomain()
386         {
387             if (_contextType != DirectoryContextType.Forest)
388                 return false;
389 
390             if (!_validated)
391             {
392                 _contextIsValid = IsContextValid(this, DirectoryContextType.Forest);
393                 _validated = true;
394             }
395             return _contextIsValid;
396         }
397 
isDomain()398         internal bool isDomain()
399         {
400             if (_contextType != DirectoryContextType.Domain)
401                 return false;
402 
403             if (!_validated)
404             {
405                 _contextIsValid = IsContextValid(this, DirectoryContextType.Domain);
406                 _validated = true;
407             }
408             return _contextIsValid;
409         }
410 
isNdnc()411         internal bool isNdnc()
412         {
413             if (_contextType != DirectoryContextType.ApplicationPartition)
414                 return false;
415 
416             if (!_validated)
417             {
418                 _contextIsValid = IsContextValid(this, DirectoryContextType.ApplicationPartition);
419                 _validated = true;
420             }
421             return _contextIsValid;
422         }
423 
isServer()424         internal bool isServer()
425         {
426             if (_contextType != DirectoryContextType.DirectoryServer)
427                 return false;
428 
429             if (!_validated)
430             {
431                 _contextIsValid = IsContextValid(this, DirectoryContextType.DirectoryServer);
432                 _validated = true;
433             }
434             return _contextIsValid;
435         }
436 
isADAMConfigSet()437         internal bool isADAMConfigSet()
438         {
439             if (_contextType != DirectoryContextType.ConfigurationSet)
440                 return false;
441 
442             if (!_validated)
443             {
444                 _contextIsValid = IsContextValid(this, DirectoryContextType.ConfigurationSet);
445                 _validated = true;
446             }
447             return _contextIsValid;
448         }
449 
450         //
451         // this method is called when the forest name is explicitly specified
452         // and we want to check if that matches the current logged on forest
453         //
isCurrentForest()454         internal bool isCurrentForest()
455         {
456             bool result = false;
457 
458             Debug.Assert(_name != null);
459             DomainControllerInfo domainControllerInfo = Locator.GetDomainControllerInfo(null, _name, null, (long)(PrivateLocatorFlags.DirectoryServicesRequired | PrivateLocatorFlags.ReturnDNSName));
460 
461             DomainControllerInfo currentDomainControllerInfo;
462             string loggedOnDomain = GetLoggedOnDomain();
463 
464             int errorCode = Locator.DsGetDcNameWrapper(null, loggedOnDomain, null, (long)(PrivateLocatorFlags.DirectoryServicesRequired | PrivateLocatorFlags.ReturnDNSName), out currentDomainControllerInfo);
465 
466             if (errorCode == 0)
467             {
468                 Debug.Assert(domainControllerInfo.DnsForestName != null);
469                 Debug.Assert(currentDomainControllerInfo.DnsForestName != null);
470 
471                 result = (Utils.Compare(domainControllerInfo.DnsForestName, currentDomainControllerInfo.DnsForestName) == 0);
472             }
473             //
474             // if there is no forest associated with the logged on domain, then we return false
475             //
476             else if (errorCode != NativeMethods.ERROR_NO_SUCH_DOMAIN)
477             {
478                 throw ExceptionHelper.GetExceptionFromErrorCode(errorCode);
479             }
480 
481             return result;
482         }
483 
useServerBind()484         internal bool useServerBind()
485         {
486             return ((ContextType == DirectoryContextType.DirectoryServer) || (ContextType == DirectoryContextType.ConfigurationSet));
487         }
488 
GetServerName()489         internal string GetServerName()
490         {
491             if (serverName == null)
492             {
493                 switch (_contextType)
494                 {
495                     case DirectoryContextType.ConfigurationSet:
496                         {
497                             AdamInstance adamInst = ConfigurationSet.FindAnyAdamInstance(this);
498                             try
499                             {
500                                 serverName = adamInst.Name;
501                             }
502                             finally
503                             {
504                                 adamInst.Dispose();
505                             }
506                             break;
507                         }
508                     case DirectoryContextType.Domain:
509                     case DirectoryContextType.Forest:
510                         {
511                             //
512                             // if the target is not specified OR
513                             // if the forest name was explicitly specified and the forest is the same as the current forest
514                             // we want to find a DC in the current domain
515                             //
516                             if ((_name == null) || ((_contextType == DirectoryContextType.Forest) && (isCurrentForest())))
517                             {
518                                 serverName = GetLoggedOnDomain();
519                             }
520                             else
521                             {
522                                 serverName = GetDnsDomainName(_name);
523                             }
524                             break;
525                         }
526                     case DirectoryContextType.ApplicationPartition:
527                         {
528                             // if this is an appNC the target should not be null
529                             Debug.Assert(_name != null);
530 
531                             serverName = _name;
532                             break;
533                         }
534                     case DirectoryContextType.DirectoryServer:
535                         {
536                             // this should not happen (We should have checks for this earlier itself)
537                             Debug.Assert(_name != null);
538                             serverName = _name;
539                             break;
540                         }
541                     default:
542                         {
543                             Debug.Fail("DirectoryContext::GetServerName - Unknown contextType");
544                             break;
545                         }
546                 }
547             }
548 
549             return serverName;
550         }
551 
GetLoggedOnDomain()552         internal static string GetLoggedOnDomain()
553         {
554             string domainName = null;
555 
556             NegotiateCallerNameRequest requestBuffer = new NegotiateCallerNameRequest();
557             int requestBufferLength = (int)Marshal.SizeOf(requestBuffer);
558 
559             IntPtr pResponseBuffer = IntPtr.Zero;
560             NegotiateCallerNameResponse responseBuffer = new NegotiateCallerNameResponse();
561             int responseBufferLength;
562             int protocolStatus;
563             int result;
564 
565             LsaLogonProcessSafeHandle lsaHandle;
566 
567             //
568             // since we are using safe handles, we don't need to explicitly call NativeMethods.LsaDeregisterLogonProcess(lsaHandle)
569             //
570             result = NativeMethods.LsaConnectUntrusted(out lsaHandle);
571 
572             if (result == 0)
573             {
574                 //
575                 // initialize the request buffer
576                 //
577                 requestBuffer.messageType = NativeMethods.NegGetCallerName;
578 
579                 result = NativeMethods.LsaCallAuthenticationPackage(lsaHandle, 0, requestBuffer, requestBufferLength, out pResponseBuffer, out responseBufferLength, out protocolStatus);
580 
581                 try
582                 {
583                     if (result == 0 && protocolStatus == 0)
584                     {
585                         Marshal.PtrToStructure(pResponseBuffer, responseBuffer);
586 
587                         //
588                         // callerName is of the form domain\username
589                         //
590                         Debug.Assert((responseBuffer.callerName != null), "NativeMethods.LsaCallAuthenticationPackage returned null callerName.");
591                         int index = responseBuffer.callerName.IndexOf('\\');
592                         Debug.Assert((index != -1), "NativeMethods.LsaCallAuthenticationPackage returned callerName not in domain\\username format.");
593                         domainName = responseBuffer.callerName.Substring(0, index);
594                     }
595                     else
596                     {
597                         if (result == NativeMethods.STATUS_QUOTA_EXCEEDED)
598                         {
599                             throw new OutOfMemoryException();
600                         }
601                         else if ((result == 0) && (UnsafeNativeMethods.LsaNtStatusToWinError(protocolStatus) == NativeMethods.ERROR_NO_SUCH_LOGON_SESSION))
602                         {
603                             // If this is a directory user, extract domain info from username
604                             if (!Utils.IsSamUser())
605                             {
606                                 WindowsIdentity identity = WindowsIdentity.GetCurrent();
607 
608                                 int index = identity.Name.IndexOf('\\');
609                                 Debug.Assert(index != -1);
610                                 domainName = identity.Name.Substring(0, index);
611                             }
612                         }
613                         else
614                         {
615                             throw ExceptionHelper.GetExceptionFromErrorCode(UnsafeNativeMethods.LsaNtStatusToWinError((result != 0) ? result : protocolStatus));
616                         }
617                     }
618                 }
619                 finally
620                 {
621                     if (pResponseBuffer != IntPtr.Zero)
622                     {
623                         NativeMethods.LsaFreeReturnBuffer(pResponseBuffer);
624                     }
625                 }
626             }
627             else if (result == NativeMethods.STATUS_QUOTA_EXCEEDED)
628             {
629                 throw new OutOfMemoryException();
630             }
631             else
632             {
633                 throw ExceptionHelper.GetExceptionFromErrorCode(UnsafeNativeMethods.LsaNtStatusToWinError(result));
634             }
635 
636             // If we're running as a local user (i.e. NT AUTHORITY\LOCAL SYSTEM, IIS APPPOOL\APPPoolIdentity, etc.),
637             // domainName will be null and we fall back to the machine's domain
638             domainName = GetDnsDomainName(domainName);
639 
640             if (domainName == null)
641             {
642                 //
643                 // we should never get to this point here since we should have already verified that the context is valid
644                 // by the time we get to this point
645                 //
646                 throw new ActiveDirectoryOperationException(SR.ContextNotAssociatedWithDomain);
647             }
648 
649             return domainName;
650         }
651 
GetDnsDomainName(string domainName)652         internal static string GetDnsDomainName(string domainName)
653         {
654             DomainControllerInfo domainControllerInfo;
655             int errorCode = 0;
656 
657             //
658             // Locator.DsGetDcNameWrapper internally passes the ReturnDNSName flag when calling DsGetDcName
659             //
660             errorCode = Locator.DsGetDcNameWrapper(null, domainName, null, (long)PrivateLocatorFlags.DirectoryServicesRequired, out domainControllerInfo);
661             if (errorCode == NativeMethods.ERROR_NO_SUCH_DOMAIN)
662             {
663                 // try again with force rediscovery
664                 errorCode = Locator.DsGetDcNameWrapper(null, domainName, null, (long)((long)PrivateLocatorFlags.DirectoryServicesRequired | (long)LocatorOptions.ForceRediscovery), out domainControllerInfo);
665                 if (errorCode == NativeMethods.ERROR_NO_SUCH_DOMAIN)
666                 {
667                     return null;
668                 }
669                 else if (errorCode != 0)
670                 {
671                     throw ExceptionHelper.GetExceptionFromErrorCode(errorCode);
672                 }
673             }
674             else if (errorCode != 0)
675             {
676                 throw ExceptionHelper.GetExceptionFromErrorCode(errorCode);
677             }
678 
679             Debug.Assert(domainControllerInfo != null);
680             Debug.Assert(domainControllerInfo.DomainName != null);
681 
682             return domainControllerInfo.DomainName;
683         }
684 
GetLibraryHandle()685         private static void GetLibraryHandle()
686         {
687             // first get AD handle
688             string systemPath = Environment.SystemDirectory;
689             IntPtr tempHandle = UnsafeNativeMethods.LoadLibrary(systemPath + "\\ntdsapi.dll");
690             if (tempHandle == (IntPtr)0)
691             {
692                 throw ExceptionHelper.GetExceptionFromErrorCode(Marshal.GetLastWin32Error());
693             }
694             else
695             {
696                 ADHandle = new LoadLibrarySafeHandle(tempHandle);
697             }
698 
699             // not get the ADAM handle
700             // got to the windows\adam directory
701             DirectoryInfo windowsDirectory = Directory.GetParent(systemPath);
702             tempHandle = UnsafeNativeMethods.LoadLibrary(windowsDirectory.FullName + "\\ADAM\\ntdsapi.dll");
703             if (tempHandle == (IntPtr)0)
704             {
705                 ADAMHandle = ADHandle;
706             }
707             else
708             {
709                 ADAMHandle = new LoadLibrarySafeHandle(tempHandle);
710             }
711         }
712 
713         #endregion private methods
714     }
715 }
716