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.Collections; 6 using System.Diagnostics; 7 using System.Runtime.InteropServices; 8 9 namespace System.DirectoryServices.ActiveDirectory 10 { 11 public class GlobalCatalog : DomainController 12 { 13 // private variables 14 private ActiveDirectorySchema _schema = null; 15 private bool _disabled = false; 16 17 #region constructors GlobalCatalog(DirectoryContext context, string globalCatalogName)18 internal GlobalCatalog(DirectoryContext context, string globalCatalogName) 19 : base(context, globalCatalogName) 20 { } 21 GlobalCatalog(DirectoryContext context, string globalCatalogName, DirectoryEntryManager directoryEntryMgr)22 internal GlobalCatalog(DirectoryContext context, string globalCatalogName, DirectoryEntryManager directoryEntryMgr) 23 : base(context, globalCatalogName, directoryEntryMgr) 24 { } 25 #endregion constructors 26 27 #region public methods 28 GetGlobalCatalog(DirectoryContext context)29 public static GlobalCatalog GetGlobalCatalog(DirectoryContext context) 30 { 31 string gcDnsName = null; 32 bool isGlobalCatalog = false; 33 DirectoryEntryManager directoryEntryMgr = null; 34 35 // check that the context argument is not null 36 if (context == null) 37 throw new ArgumentNullException("context"); 38 39 // target should be GC 40 if (context.ContextType != DirectoryContextType.DirectoryServer) 41 { 42 throw new ArgumentException(SR.TargetShouldBeGC, "context"); 43 } 44 45 // target should be a server 46 if (!(context.isServer())) 47 { 48 throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.GCNotFound , context.Name), typeof(GlobalCatalog), context.Name); 49 } 50 51 // work with copy of the context 52 context = new DirectoryContext(context); 53 54 try 55 { 56 // Get dns name of the dc 57 // by binding to root dse and getting the "dnsHostName" attribute 58 // (also check that the "isGlobalCatalogReady" attribute is true) 59 directoryEntryMgr = new DirectoryEntryManager(context); 60 DirectoryEntry rootDSE = DirectoryEntryManager.GetDirectoryEntry(context, WellKnownDN.RootDSE); 61 if (!Utils.CheckCapability(rootDSE, Capability.ActiveDirectory)) 62 { 63 throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.GCNotFound , context.Name), typeof(GlobalCatalog), context.Name); 64 } 65 66 gcDnsName = (string)PropertyManager.GetPropertyValue(context, rootDSE, PropertyManager.DnsHostName); 67 isGlobalCatalog = (bool)Boolean.Parse((string)PropertyManager.GetPropertyValue(context, rootDSE, PropertyManager.IsGlobalCatalogReady)); 68 if (!isGlobalCatalog) 69 { 70 throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.GCNotFound , context.Name), typeof(GlobalCatalog), context.Name); 71 } 72 } 73 catch (COMException e) 74 { 75 int errorCode = e.ErrorCode; 76 77 if (errorCode == unchecked((int)0x8007203a)) 78 { 79 throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.GCNotFound , context.Name), typeof(GlobalCatalog), context.Name); 80 } 81 else 82 { 83 throw ExceptionHelper.GetExceptionFromCOMException(context, e); 84 } 85 } 86 87 return new GlobalCatalog(context, gcDnsName, directoryEntryMgr); 88 } 89 FindOne(DirectoryContext context)90 public static new GlobalCatalog FindOne(DirectoryContext context) 91 { 92 if (context == null) 93 { 94 throw new ArgumentNullException("context"); 95 } 96 97 if (context.ContextType != DirectoryContextType.Forest) 98 { 99 throw new ArgumentException(SR.TargetShouldBeForest, "context"); 100 } 101 102 return FindOneWithCredentialValidation(context, null, 0); 103 } 104 FindOne(DirectoryContext context, string siteName)105 public static new GlobalCatalog FindOne(DirectoryContext context, string siteName) 106 { 107 if (context == null) 108 { 109 throw new ArgumentNullException("context"); 110 } 111 112 if (context.ContextType != DirectoryContextType.Forest) 113 { 114 throw new ArgumentException(SR.TargetShouldBeForest, "context"); 115 } 116 117 if (siteName == null) 118 { 119 throw new ArgumentNullException("siteName"); 120 } 121 122 return FindOneWithCredentialValidation(context, siteName, 0); 123 } 124 FindOne(DirectoryContext context, LocatorOptions flag)125 public static new GlobalCatalog FindOne(DirectoryContext context, LocatorOptions flag) 126 { 127 if (context == null) 128 { 129 throw new ArgumentNullException("context"); 130 } 131 132 if (context.ContextType != DirectoryContextType.Forest) 133 { 134 throw new ArgumentException(SR.TargetShouldBeForest, "context"); 135 } 136 137 return FindOneWithCredentialValidation(context, null, flag); 138 } 139 FindOne(DirectoryContext context, string siteName, LocatorOptions flag)140 public static new GlobalCatalog FindOne(DirectoryContext context, string siteName, LocatorOptions flag) 141 { 142 if (context == null) 143 { 144 throw new ArgumentNullException("context"); 145 } 146 147 if (context.ContextType != DirectoryContextType.Forest) 148 { 149 throw new ArgumentException(SR.TargetShouldBeForest, "context"); 150 } 151 152 if (siteName == null) 153 { 154 throw new ArgumentNullException("siteName"); 155 } 156 157 return FindOneWithCredentialValidation(context, siteName, flag); 158 } 159 FindAll(DirectoryContext context)160 public static new GlobalCatalogCollection FindAll(DirectoryContext context) 161 { 162 if (context == null) 163 { 164 throw new ArgumentNullException("context"); 165 } 166 167 if (context.ContextType != DirectoryContextType.Forest) 168 { 169 throw new ArgumentException(SR.TargetShouldBeForest, "context"); 170 } 171 172 // work with copy of the context 173 context = new DirectoryContext(context); 174 175 return FindAllInternal(context, null); 176 } 177 FindAll(DirectoryContext context, string siteName)178 public static new GlobalCatalogCollection FindAll(DirectoryContext context, string siteName) 179 { 180 if (context == null) 181 { 182 throw new ArgumentNullException("context"); 183 } 184 185 if (context.ContextType != DirectoryContextType.Forest) 186 { 187 throw new ArgumentException(SR.TargetShouldBeForest, "context"); 188 } 189 190 if (siteName == null) 191 { 192 throw new ArgumentNullException("siteName"); 193 } 194 195 // work with copy of the context 196 context = new DirectoryContext(context); 197 198 return FindAllInternal(context, siteName); 199 } 200 EnableGlobalCatalog()201 public override GlobalCatalog EnableGlobalCatalog() 202 { 203 CheckIfDisposed(); 204 throw new InvalidOperationException(SR.CannotPerformOnGCObject); 205 } 206 DisableGlobalCatalog()207 public DomainController DisableGlobalCatalog() 208 { 209 CheckIfDisposed(); 210 CheckIfDisabled(); 211 212 // bind to the server object 213 DirectoryEntry serverNtdsaEntry = directoryEntryMgr.GetCachedDirectoryEntry(NtdsaObjectName); 214 215 // reset the NTDSDSA_OPT_IS_GC flag on the "options" property 216 int options = 0; 217 218 try 219 { 220 if (serverNtdsaEntry.Properties[PropertyManager.Options].Value != null) 221 { 222 options = (int)serverNtdsaEntry.Properties[PropertyManager.Options].Value; 223 } 224 225 serverNtdsaEntry.Properties[PropertyManager.Options].Value = options & (~1); 226 serverNtdsaEntry.CommitChanges(); 227 } 228 catch (COMException e) 229 { 230 throw ExceptionHelper.GetExceptionFromCOMException(context, e); 231 } 232 233 // mark as disbaled 234 _disabled = true; 235 236 // return a domain controller object 237 return new DomainController(context, Name); 238 } 239 IsGlobalCatalog()240 public override bool IsGlobalCatalog() 241 { 242 CheckIfDisposed(); 243 CheckIfDisabled(); 244 245 // since this is a global catalog object, this should always return true 246 return true; 247 } 248 FindAllProperties()249 public ReadOnlyActiveDirectorySchemaPropertyCollection FindAllProperties() 250 { 251 CheckIfDisposed(); 252 CheckIfDisabled(); 253 254 // create an ActiveDirectorySchema object 255 if (_schema == null) 256 { 257 string schemaNC = null; 258 try 259 { 260 schemaNC = directoryEntryMgr.ExpandWellKnownDN(WellKnownDN.SchemaNamingContext); 261 } 262 catch (COMException e) 263 { 264 throw ExceptionHelper.GetExceptionFromCOMException(context, e); 265 } 266 DirectoryContext schemaContext = Utils.GetNewDirectoryContext(Name, DirectoryContextType.DirectoryServer, context); 267 _schema = new ActiveDirectorySchema(context, schemaNC); 268 } 269 270 // return the global catalog replicated properties 271 return _schema.FindAllProperties(PropertyTypes.InGlobalCatalog); 272 } 273 GetDirectorySearcher()274 public override DirectorySearcher GetDirectorySearcher() 275 { 276 CheckIfDisposed(); 277 CheckIfDisabled(); 278 279 return InternalGetDirectorySearcher(); 280 } 281 282 #endregion public methods 283 284 #region private methods 285 CheckIfDisabled()286 private void CheckIfDisabled() 287 { 288 if (_disabled) 289 { 290 throw new InvalidOperationException(SR.GCDisabled); 291 } 292 } 293 FindOneWithCredentialValidation(DirectoryContext context, string siteName, LocatorOptions flag)294 internal static new GlobalCatalog FindOneWithCredentialValidation(DirectoryContext context, string siteName, LocatorOptions flag) 295 { 296 GlobalCatalog gc; 297 bool retry = false; 298 bool credsValidated = false; 299 300 // work with copy of the context 301 context = new DirectoryContext(context); 302 303 // authenticate against this GC to validate the credentials 304 gc = FindOneInternal(context, context.Name, siteName, flag); 305 try 306 { 307 ValidateCredential(gc, context); 308 credsValidated = true; 309 } 310 catch (COMException e) 311 { 312 if (e.ErrorCode == unchecked((int)0x8007203a)) 313 { 314 // server is down , so try again with force rediscovery if the flags did not already contain force rediscovery 315 if ((flag & LocatorOptions.ForceRediscovery) == 0) 316 { 317 retry = true; 318 } 319 else 320 { 321 throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.GCNotFoundInForest , context.Name), typeof(GlobalCatalog), null); 322 } 323 } 324 else 325 { 326 throw ExceptionHelper.GetExceptionFromCOMException(context, e); 327 } 328 } 329 finally 330 { 331 if (!credsValidated) 332 { 333 gc.Dispose(); 334 } 335 } 336 337 if (retry) 338 { 339 credsValidated = false; 340 gc = FindOneInternal(context, context.Name, siteName, flag | LocatorOptions.ForceRediscovery); 341 try 342 { 343 ValidateCredential(gc, context); 344 credsValidated = true; 345 } 346 catch (COMException e) 347 { 348 if (e.ErrorCode == unchecked((int)0x8007203a)) 349 { 350 // server is down 351 throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.GCNotFoundInForest , context.Name), typeof(GlobalCatalog), null); 352 } 353 else 354 { 355 throw ExceptionHelper.GetExceptionFromCOMException(context, e); 356 } 357 } 358 finally 359 { 360 if (!credsValidated) 361 { 362 gc.Dispose(); 363 } 364 } 365 } 366 367 return gc; 368 } 369 FindOneInternal(DirectoryContext context, string forestName, string siteName, LocatorOptions flag)370 internal static new GlobalCatalog FindOneInternal(DirectoryContext context, string forestName, string siteName, LocatorOptions flag) 371 { 372 DomainControllerInfo domainControllerInfo; 373 int errorCode = 0; 374 375 if (siteName != null && siteName.Length == 0) 376 { 377 throw new ArgumentException(SR.EmptyStringParameter, "siteName"); 378 } 379 380 // check that the flags passed have only the valid bits set 381 if (((long)flag & (~((long)LocatorOptions.AvoidSelf | (long)LocatorOptions.ForceRediscovery | (long)LocatorOptions.KdcRequired | (long)LocatorOptions.TimeServerRequired | (long)LocatorOptions.WriteableRequired))) != 0) 382 { 383 throw new ArgumentException(SR.InvalidFlags, "flag"); 384 } 385 386 if (forestName == null) 387 { 388 // get the dns name of the logged on forest 389 DomainControllerInfo tempDomainControllerInfo; 390 int error = Locator.DsGetDcNameWrapper(null, DirectoryContext.GetLoggedOnDomain(), null, (long)PrivateLocatorFlags.DirectoryServicesRequired, out tempDomainControllerInfo); 391 392 if (error == NativeMethods.ERROR_NO_SUCH_DOMAIN) 393 { 394 // throw not found exception 395 throw new ActiveDirectoryObjectNotFoundException(SR.ContextNotAssociatedWithDomain, typeof(GlobalCatalog), null); 396 } 397 else if (error != 0) 398 { 399 throw ExceptionHelper.GetExceptionFromErrorCode(errorCode); 400 } 401 402 Debug.Assert(tempDomainControllerInfo.DnsForestName != null); 403 forestName = tempDomainControllerInfo.DnsForestName; 404 } 405 406 // call DsGetDcName 407 errorCode = Locator.DsGetDcNameWrapper(null, forestName, siteName, (long)flag | (long)(PrivateLocatorFlags.GCRequired | PrivateLocatorFlags.DirectoryServicesRequired), out domainControllerInfo); 408 409 if (errorCode == NativeMethods.ERROR_NO_SUCH_DOMAIN) 410 { 411 throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.GCNotFoundInForest , forestName), typeof(GlobalCatalog), null); 412 } 413 // this can only occur when flag is being explicitly passed (since the flags that we pass internally are valid) 414 if (errorCode == NativeMethods.ERROR_INVALID_FLAGS) 415 { 416 throw new ArgumentException(SR.InvalidFlags, "flag"); 417 } 418 else if (errorCode != 0) 419 { 420 throw ExceptionHelper.GetExceptionFromErrorCode(errorCode); 421 } 422 423 // create a GlobalCatalog object 424 // the name is returned in the form "\\servername", so skip the "\\" 425 Debug.Assert(domainControllerInfo.DomainControllerName.Length > 2); 426 string globalCatalogName = domainControllerInfo.DomainControllerName.Substring(2); 427 428 // create a new context object for the global catalog 429 DirectoryContext gcContext = Utils.GetNewDirectoryContext(globalCatalogName, DirectoryContextType.DirectoryServer, context); 430 431 return new GlobalCatalog(gcContext, globalCatalogName); 432 } 433 FindAllInternal(DirectoryContext context, string siteName)434 internal static GlobalCatalogCollection FindAllInternal(DirectoryContext context, string siteName) 435 { 436 ArrayList gcList = new ArrayList(); 437 438 if (siteName != null && siteName.Length == 0) 439 { 440 throw new ArgumentException(SR.EmptyStringParameter, "siteName"); 441 } 442 443 foreach (string gcName in Utils.GetReplicaList(context, null /* not specific to any partition */, siteName, false /* isDefaultNC */, false /* isADAM */, true /* mustBeGC */)) 444 { 445 DirectoryContext gcContext = Utils.GetNewDirectoryContext(gcName, DirectoryContextType.DirectoryServer, context); 446 gcList.Add(new GlobalCatalog(gcContext, gcName)); 447 } 448 449 return new GlobalCatalogCollection(gcList); 450 } 451 InternalGetDirectorySearcher()452 private DirectorySearcher InternalGetDirectorySearcher() 453 { 454 DirectoryEntry de = new DirectoryEntry("GC://" + Name); 455 456 de.AuthenticationType = Utils.DefaultAuthType | AuthenticationTypes.ServerBind; 457 458 de.Username = context.UserName; 459 de.Password = context.Password; 460 461 return new DirectorySearcher(de); 462 } 463 464 #endregion 465 } 466 } 467