internal protected AdvancedFilters(Principal p) { _p = p; }
internal override bool IsMemberOfInStore(GroupPrincipal g, Principal p) { Debug.Fail("SAMStoreCtx.IsMemberOfInStore: Shouldn't be here."); return(false); }
// // Cross-store support // // Given a native store object that represents a "foreign" principal (e.g., a FPO object in this store that // represents a pointer to another store), maps that representation to the other store's StoreCtx and returns // a Principal from that other StoreCtx. The implementation of this method is highly dependent on the // details of the particular store, and must have knowledge not only of this StoreCtx, but also of how to // interact with other StoreCtxs to fulfill the request. // // This method is typically used by ResultSet implementations, when they're iterating over a collection // (e.g., of group membership) and encounter an entry that represents a foreign principal. internal override Principal ResolveCrossStoreRefToPrincipal(object o) { Debug.Assert(o is DirectoryEntry); // Get the SID of the foreign principal DirectoryEntry foreignDE = (DirectoryEntry)o; if (foreignDE.Properties["objectSid"].Count == 0) { GlobalDebug.WriteLineIf(GlobalDebug.Warn, "SAMStoreCtx", "ResolveCrossStoreRefToPrincipal: no objectSid found"); throw new PrincipalOperationException(SR.SAMStoreCtxCantRetrieveObjectSidForCrossStore); } Debug.Assert(foreignDE.Properties["objectSid"].Count == 1); byte[] sid = (byte[])foreignDE.Properties["objectSid"].Value; // Ask the OS to resolve the SID to its target. int accountUsage = 0; string name; string domainName; int err = Utils.LookupSid(this.MachineUserSuppliedName, _credentials, sid, out name, out domainName, out accountUsage); if (err != 0) { GlobalDebug.WriteLineIf(GlobalDebug.Warn, "SAMStoreCtx", "ResolveCrossStoreRefToPrincipal: LookupSid failed, err={0}, server={1}", err, this.MachineUserSuppliedName); throw new PrincipalOperationException( SR.Format(SR.SAMStoreCtxCantResolveSidForCrossStore, err)); } GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMStoreCtx", "ResolveCrossStoreRefToPrincipal: LookupSid found {0} in {1}", name, domainName); // Since this is SAM, the remote principal must be an AD principal. // Build a PrincipalContext for the store which owns the principal // Use the ad default options so we turn sign and seal back on. #if USE_CTX_CACHE PrincipalContext remoteCtx = SDSCache.Domain.GetContext(domainName, _credentials, DefaultContextOptions.ADDefaultContextOption); #else PrincipalContext remoteCtx = new PrincipalContext( ContextType.Domain, domainName, null, (this.credentials != null ? credentials.UserName : null), (this.credentials != null ? credentials.Password : null), DefaultContextOptions.ADDefaultContextOption); #endif SecurityIdentifier sidObj = new SecurityIdentifier(sid, 0); Principal p = remoteCtx.QueryCtx.FindPrincipalByIdentRef( typeof(Principal), UrnScheme.SidScheme, sidObj.ToString(), DateTime.UtcNow); if (p != null) { return(p); } else { GlobalDebug.WriteLineIf(GlobalDebug.Warn, "SAMStoreCtx", "ResolveCrossStoreRefToPrincipal: no matching principal"); throw new PrincipalOperationException(SR.SAMStoreCtxFailedFindCrossStoreTarget); } }
internal static void SingleScalarFromDirectoryEntry <T>(dSPropertyCollection properties, string suggestedProperty, Principal p, string propertyName) { if (properties[suggestedProperty].Count != 0 && properties[suggestedProperty][0] != null) { p.LoadValueIntoProperty(propertyName, (T)properties[suggestedProperty][0]); } }
internal override void Move(StoreCtx originalStore, Principal p) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMStoreCtx", "Move"); }
protected internal AdvancedFilters(Principal p) { this.p = p; }
internal static void AccountControlToDirectoryEntry(Principal p, string propertyName, DirectoryEntry de, string suggestedProperty, bool isSAM, bool isUnpersisted) { uint num; bool valueForProperty = (bool)p.GetValueForProperty(propertyName); if (de.Properties[suggestedProperty].Count <= 0) { throw new PrincipalOperationException(StringResources.ADStoreCtxUnableToReadExistingAccountControlFlagsForUpdate); } else { int item = (int)de.Properties[suggestedProperty][0]; if (!isSAM && de.Properties["msDS-User-Account-Control-Computed"].Count > 0) { item = item | (int)de.Properties["msDS-User-Account-Control-Computed"][0]; } string str = propertyName; string str1 = str; if (str != null) { if (str1 == "AuthenticablePrincipal.Enabled") { if (!isUnpersisted || isSAM) { num = 2; valueForProperty = !valueForProperty; if (!valueForProperty) { Utils.ClearBit(ref item, num); } else { Utils.SetBit(ref item, num); } de.Properties[suggestedProperty].Value = (object)item; return; } else { num = 0; if (!valueForProperty) { Utils.ClearBit(ref item, num); } else { Utils.SetBit(ref item, num); } de.Properties[suggestedProperty].Value = (object)item; return; } } else if (str1 == "AuthenticablePrincipal.AccountInfo.SmartcardLogonRequired") { num = 0x40000; if (!valueForProperty) { Utils.ClearBit(ref item, num); } else { Utils.SetBit(ref item, num); } de.Properties[suggestedProperty].Value = (object)item; return; } else if (str1 == "AuthenticablePrincipal.AccountInfo.DelegationPermitted") { num = 0x100000; valueForProperty = !valueForProperty; if (!valueForProperty) { Utils.ClearBit(ref item, num); } else { Utils.SetBit(ref item, num); } de.Properties[suggestedProperty].Value = (object)item; return; } else if (str1 == "AuthenticablePrincipal.PasswordInfo.PasswordNotRequired") { num = 32; if (!valueForProperty) { Utils.ClearBit(ref item, num); } else { Utils.SetBit(ref item, num); } de.Properties[suggestedProperty].Value = (object)item; return; } else if (str1 == "AuthenticablePrincipal.PasswordInfo.PasswordNeverExpires") { num = 0x10000; if (!valueForProperty) { Utils.ClearBit(ref item, num); } else { Utils.SetBit(ref item, num); } de.Properties[suggestedProperty].Value = (object)item; return; } else if (str1 == "AuthenticablePrincipal.PasswordInfo.AllowReversiblePasswordEncryption") { num = 128; if (!valueForProperty) { Utils.ClearBit(ref item, num); } else { Utils.SetBit(ref item, num); } de.Properties[suggestedProperty].Value = (object)item; return; } else if (str1 == "AuthenticablePrincipal.PasswordInfo.UserCannotChangePassword") { if (!isSAM) { num = 0; if (!valueForProperty) { Utils.ClearBit(ref item, num); } else { Utils.SetBit(ref item, num); } de.Properties[suggestedProperty].Value = (object)item; return; } num = 64; if (!valueForProperty) { Utils.ClearBit(ref item, num); } else { Utils.SetBit(ref item, num); } de.Properties[suggestedProperty].Value = (object)item; return; } num = 0; if (!valueForProperty) { Utils.ClearBit(ref item, num); } else { Utils.SetBit(ref item, num); } de.Properties[suggestedProperty].Value = (object)item; return; } else { num = 0; if (!valueForProperty) { Utils.ClearBit(ref item, num); } else { Utils.SetBit(ref item, num); } de.Properties[suggestedProperty].Value = (object)item; return; } if (!valueForProperty) { Utils.ClearBit(ref item, num); } else { Utils.SetBit(ref item, num); } de.Properties[suggestedProperty].Value = item; return; } }
// Can the given member be removed from the specified group? If not, also returns // a string containing a human-readable explanation of why not, suitable for use in an exception. internal abstract bool CanGroupMemberBeRemoved(GroupPrincipal g, Principal member, out string explanationForFailure);
// // Data Validation // // Validiate the passed property name to determine if it is valid for the store and Principal type. // used by the principal objects to determine if a property is valid in the property before // save is called. internal abstract bool IsValidProperty(Principal p, string propertyName);
// Get groups of which p is a member, using AuthZ S4U APIs for recursive membership internal abstract ResultSet GetGroupsMemberOfAZ(Principal p);
internal abstract bool IsMemberOfInStore(GroupPrincipal g, Principal p);
// Get groups from this ctx which contain a principal corresponding to foreignPrincipal // (which is a principal from foreignContext) internal abstract ResultSet GetGroupsMemberOf(Principal foreignPrincipal, StoreCtx foreignContext);
internal abstract bool AccessCheck(Principal p, PrincipalAccessMask targetPermission);
// Returns a type indicating the type of object that would be returned as the wormhole for the specified // Principal. For some StoreCtxs, this method may always return a constant (e.g., typeof(DirectoryEntry) // for ADStoreCtx). For others, it may vary depending on the Principal passed in. internal abstract Type NativeType(Principal p);
// Loads only the psecified property into the principal object. The object should have already been persisted or searched for this to happen. internal abstract void Load(Principal p, string principalPropertyName);
private void BuildFilterSet(Principal p, string[] propertySet, QbeFilterDescription qbeFilterDescription) { foreach (string propertyName in propertySet) { if (p.GetChangeStatusForProperty(propertyName)) { // Property has changed. Add it to the filter set. object value = p.GetValueForProperty(propertyName); GlobalDebug.WriteLineIf( GlobalDebug.Info, "StoreCtx", "BuildFilterSet: type={0}, property name={1}, property value={2} of type {3}", p.GetType().ToString(), propertyName, value.ToString(), value.GetType().ToString()); // Build the right filter based on type of the property value if (value is PrincipalValueCollection <string> ) { PrincipalValueCollection <string> trackingList = (PrincipalValueCollection <string>)value; foreach (string s in trackingList.Inserted) { object filter = FilterFactory.CreateFilter(propertyName); ((FilterBase)filter).Value = (string)s; qbeFilterDescription.FiltersToApply.Add(filter); } } else if (value is X509Certificate2Collection) { // Since QBE filter objects are always unpersisted, any certs in the collection // must have been inserted by the application. X509Certificate2Collection certCollection = (X509Certificate2Collection)value; foreach (X509Certificate2 cert in certCollection) { object filter = FilterFactory.CreateFilter(propertyName); ((FilterBase)filter).Value = (X509Certificate2)cert; qbeFilterDescription.FiltersToApply.Add(filter); } } else { // It's not one of the multivalued cases. Try the scalar cases. object filter = FilterFactory.CreateFilter(propertyName); if (value == null) { ((FilterBase)filter).Value = null; } else if (value is bool) { ((FilterBase)filter).Value = (bool)value; } else if (value is string) { ((FilterBase)filter).Value = (string)value; } else if (value is GroupScope) { ((FilterBase)filter).Value = (GroupScope)value; } else if (value is byte[]) { ((FilterBase)filter).Value = (byte[])value; } else if (value is Nullable <DateTime> ) { ((FilterBase)filter).Value = (Nullable <DateTime>)value; } else if (value is ExtensionCache) { ((FilterBase)filter).Value = (ExtensionCache)value; } else if (value is QbeMatchType) { ((FilterBase)filter).Value = (QbeMatchType)value; } else { // Internal error. Didn't match either the known multivalued or scalar cases. Debug.Fail(String.Format( CultureInfo.CurrentCulture, "StoreCtx.BuildFilterSet: fell off end looking for {0} of type {1}", propertyName, value.GetType().ToString() )); } qbeFilterDescription.FiltersToApply.Add(filter); } } } }
// Pushes the query represented by the QBE filter into the PrincipalSearcher's underlying native // searcher object (creating a fresh native searcher and assigning it to the PrincipalSearcher if one // doesn't already exist) and returns the native searcher. // If the PrincipalSearcher does not have a query filter set (PrincipalSearcher.QueryFilter == null), // produces a query that will match all principals in the store. // // For stores which don't have a native searcher (SAM), the StoreCtx // is free to create any type of object it chooses to use as its internal representation of the query. // // Also adds in any clauses to the searcher to ensure that only principals, not mere // contacts, are retrieved from the store. internal override object PushFilterToNativeSearcher(PrincipalSearcher ps) { // This is the first time we're being called on this principal. Create a fresh searcher. if (ps.UnderlyingSearcher == null) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADStoreCtx", "PushFilterToNativeSearcher: creating fresh DirectorySearcher"); ps.UnderlyingSearcher = new DirectorySearcher(this.ctxBase); ((DirectorySearcher)ps.UnderlyingSearcher).PageSize = ps.PageSize; ((DirectorySearcher)ps.UnderlyingSearcher).ServerTimeLimit = new TimeSpan(0, 0, 30); // 30 seconds } DirectorySearcher ds = (DirectorySearcher)ps.UnderlyingSearcher; Principal qbeFilter = ps.QueryFilter; StringBuilder ldapFilter = new StringBuilder(); if (qbeFilter == null) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADStoreCtx", "PushFilterToNativeSearcher: no qbeFilter specified"); // No filter specified. Search for all principals (all users, computers, groups). ldapFilter.Append("(|(objectClass=user)(objectClass=computer)(objectClass=group))"); } else { // // Start by appending the appropriate objectClass given the Principal type // ldapFilter.Append(GetObjectClassPortion(qbeFilter.GetType())); // // Next, fill in the properties (if any) // QbeFilterDescription filters = BuildQbeFilterDescription(qbeFilter); GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADStoreCtx", "PushFilterToNativeSearcher: using {0} filters", filters.FiltersToApply.Count); Hashtable filterTable = (Hashtable)s_filterPropertiesTable[this.MappingTableIndex]; foreach (FilterBase filter in filters.FiltersToApply) { FilterPropertyTableEntry entry = (FilterPropertyTableEntry)filterTable[filter.GetType()]; if (entry == null) { // Must be a property we don't support throw new InvalidOperationException( SR.Format( SR.StoreCtxUnsupportedPropertyForQuery, PropertyNamesExternal.GetExternalForm(filter.PropertyName))); } ldapFilter.Append(entry.converter(filter, entry.suggestedADPropertyName)); } // // Wrap off the filter // ldapFilter.Append(')'); } // We don't need any attributes returned, since we're just going to get a DirectoryEntry // for the result. Per RFC 2251, OID 1.1 == no attributes. //ds.PropertiesToLoad.Add("1.1"); BuildPropertySet(qbeFilter.GetType(), ds.PropertiesToLoad); ds.Filter = ldapFilter.ToString(); GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADStoreCtx", "PushFilterToNativeSearcher: using LDAP filter {0}", ds.Filter); return(ds); }
// // CRUD // // Used to perform the specified operation on the Principal. They also make any needed security subsystem // calls to obtain digitial signatures (e..g, to sign the Principal Extension/GroupMember Relationship for // WinFS). // // Insert() and Update() must check to make sure no properties not supported by this StoreCtx // have been set, prior to persisting the Principal. internal abstract void Insert(Principal p);
internal static void AccountControlFromDirectoryEntry(dSPropertyCollection properties, string suggestedProperty, Principal p, string propertyName, bool testCantChangePassword) { dSPropertyValueCollection item = properties[suggestedProperty]; if (item.Count != 0) { int num = (int)item[0]; bool flag = SDSUtils.StatusFromAccountControl(num, propertyName); p.LoadValueIntoProperty(propertyName, flag); } }
internal abstract void Update(Principal p);
internal static void MultiScalarFromDirectoryEntry <T>(dSPropertyCollection properties, string suggestedProperty, Principal p, string propertyName) { dSPropertyValueCollection item = properties[suggestedProperty]; List <T> ts = new List <T>(); foreach (object obj in item) { ts.Add((T)obj); } p.LoadValueIntoProperty(propertyName, ts); }
internal abstract void Delete(Principal p);
private bool MoveNextForeign() { bool needToRetry; do { needToRetry = false; GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextForeign: foreignMembers count={0}", _foreignMembers.Count); if (_foreignMembers.Count > 0) { // foreignDE is a DirectoryEntry in _this_ store representing a principal in another store DirectoryEntry foreignDE = _foreignMembers[0]; _foreignMembers.RemoveAt(0); GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextForeign: foreignDE={0}", foreignDE.Path); // foreignPrincipal is a principal from _another_ store (e.g., it's backed by an ADStoreCtx) Principal foreignPrincipal = _storeCtx.ResolveCrossStoreRefToPrincipal(foreignDE); // If we're not enumerating recursively, return the principal. // If we are enumerating recursively, and it's a group, save it off for later. if (!_recursive || !(foreignPrincipal is GroupPrincipal)) { // Return the principal. GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextForeign: setting currentForeign to {0}", foreignDE.Path); _current = null; _currentFakePrincipal = null; _currentForeign = foreignPrincipal; if (_foreignResultSet != null) { _foreignResultSet.Dispose(); } _foreignResultSet = null; return(true); } else { // Save off the group for recursive expansion, and go on to the next principal. GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextForeign: adding {0} to foreignGroups", foreignDE.Path); _foreignGroups.Add((GroupPrincipal)foreignPrincipal); needToRetry = true; continue; } } if (_foreignResultSet == null && _foreignGroups.Count > 0) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextForeign: getting foreignResultSet (foreignGroups count={0})", _foreignGroups.Count); // We're expanding recursively, and either (1) we're immediately before // the recursive expansion of the first foreign group, or (2) we just completed // the recursive expansion of a foreign group, and now are moving on to the next. Debug.Assert(_recursive == true); // Pull off a foreign group to expand. GroupPrincipal foreignGroup = _foreignGroups[0]; _foreignGroups.RemoveAt(0); // Since it's a foreign group, we don't know how to enumerate its members. So we'll // ask the group, through its StoreCtx, to do it for us. Effectively, we'll end up acting // as a proxy to the foreign group's ResultSet. _foreignResultSet = foreignGroup.GetStoreCtxToUse().GetGroupMembership(foreignGroup, true); } // We're either just beginning the recursive expansion of a foreign group, or we're continuing the expansion // that we started on a previous call to MoveNext(). if (_foreignResultSet != null) { Debug.Assert(_recursive == true); bool f = _foreignResultSet.MoveNext(); if (f) { // By setting current, currentFakePrincipal, and currentForeign to null, // CurrentAsPrincipal/CurrentAsIdentityReference will know to proxy out to foreignResultSet. GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextForeign: using foreignResultSet"); _current = null; _currentFakePrincipal = null; _currentForeign = null; return(true); } // Ran out of members in the foreign group, is there another foreign group remaining that we need // to expand? if (_foreignGroups.Count > 0) { // Yes, there is. Null out the foreignResultSet so we'll pull out the next foreign group // the next time around the loop. GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextForeign: ran out of members, using next foreignResultSet"); _foreignResultSet.Dispose(); _foreignResultSet = null; Debug.Assert(_foreignMembers.Count == 0); needToRetry = true; } else { // No, there isn't. Nothing left to do. We set foreignResultSet to null here just // to leave things in a clean state --- it shouldn't really be necessary. GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextForeign: ran out of members, nothing more to do"); _foreignResultSet.Dispose(); _foreignResultSet = null; } } }while (needToRetry); return(false); }
internal abstract void Move(StoreCtx originalStore, Principal p);
// Get groups of which p is a direct member internal override ResultSet GetGroupsMemberOf(Principal p) { // Enforced by the methods that call us Debug.Assert(p.unpersisted == false); if (!p.fakePrincipal) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMStoreCtx", "GetGroupsMemberOf: is real principal"); // No nested groups or computers as members of groups in SAM if (!(p is UserPrincipal)) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMStoreCtx", "GetGroupsMemberOf: not a user, returning empty set"); return(new EmptySet()); } Debug.Assert(p.UnderlyingObject != null); DirectoryEntry userDE = (DirectoryEntry)p.UnderlyingObject; UnsafeNativeMethods.IADsMembers iadsMembers = (UnsafeNativeMethods.IADsMembers)userDE.Invoke("Groups"); ResultSet resultSet = new SAMGroupsSet(iadsMembers, this, _ctxBase); return(resultSet); } else { // ADSI's IADsGroups doesn't work for fake principals like NT AUTHORITY\NETWORK SERVICE // We use the same SAMQuery set that we use for query-by-example, but with a different // SAMMatcher class to match groups which contain the specified principal as a member // Get the entries we'll iterate over. Write access to Children is controlled through the // ctxBaseLock, but we don't want to have to hold that lock while we're iterating over all // the child entries. So we have to clone the ctxBase --- not ideal, but it prevents // multithreading issues. DirectoryEntries entries = SDSUtils.BuildDirectoryEntry(_ctxBase.Path, _credentials, _authTypes).Children; Debug.Assert(entries != null); // The SAMQuerySet will use this to restrict the types of DirectoryEntry objects returned. List <string> schemaTypes = GetSchemaFilter(typeof(GroupPrincipal)); SecurityIdentifier principalSid = p.Sid; byte[] SidB = new byte[principalSid.BinaryLength]; principalSid.GetBinaryForm(SidB, 0); if (principalSid == null) { GlobalDebug.WriteLineIf(GlobalDebug.Warn, "SAMStoreCtx", "GetGroupsMemberOf: bad SID IC"); throw new InvalidOperationException(SR.StoreCtxNeedValueSecurityIdentityClaimToQuery); } // Create the ResultSet that will perform the client-side filtering SAMQuerySet resultSet = new SAMQuerySet( schemaTypes, entries, _ctxBase, -1, // no size limit this, new GroupMemberMatcher(SidB)); return(resultSet); } }
// // Native <--> Principal // // For modified object, pushes any changes (including IdentityClaim changes) // into the underlying store-specific object (e.g., DirectoryEntry) and returns the underlying object. // For unpersisted object, creates a underlying object if one doesn't already exist (in // Principal.UnderlyingObject), then pushes any changes into the underlying object. internal abstract object PushChangesToNative(Principal p);
// Can the given member be removed from the specified group? If not, also returns // a string containing a human-readable explanation of why not, suitable for use in an exception. internal override bool CanGroupMemberBeRemoved(GroupPrincipal g, Principal member, out string explanationForFailure) { // Always true for this type of StoreCtx explanationForFailure = null; return(true); }
// Loads the store values from p.UnderlyingObject into p, performing schema mapping as needed. internal abstract void Load(Principal p);
public static IObservable <DirectoryEntry> GetAllGroups(this Principal source, IScheduler scheduler = null) => Locator.Current.GetService <ADFacade>().GetParents(source.GetGroups().Select(principal => principal.Name), scheduler);
internal ExtensionHelper(Principal p) { _p = p; }