Skip to content

Commit c62556c

Browse files
author
Michael Cuomo
authored
[BED-6027] - Filter GPOs with Computer Configuration Setting Disabled (#241)
* WIP: Evaluated and Collect GPO Status on ReadGPOLocalGroups * chore: WIP test * chore: WIP Update Unit Test with Computer Results * fix: Add Flags to Query, Update Tests * chore: Remove gpostatus Translation from Collection
1 parent 999bf5f commit c62556c

3 files changed

Lines changed: 146 additions & 2 deletions

File tree

src/CommonLib/Processors/GPOLocalGroupProcessor.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ public async Task<ResultingGPOChanges> ReadGPOLocalGroups(string gpLink, string
131131
var result = await _utils.Query(new LdapQueryParameters() {
132132
LDAPFilter = new LdapFilter().AddAllObjects().GetFilter(),
133133
SearchScope = SearchScope.Base,
134-
Attributes = CommonProperties.GPCFileSysPath,
134+
Attributes = [LDAPProperties.GPCFileSYSPath, LDAPProperties.Flags],
135135
SearchBase = linkDn,
136136
DomainName = gpoDomain
137137
}).DefaultIfEmpty(LdapResult<IDirectoryObject>.Fail()).FirstOrDefaultAsync();
@@ -140,7 +140,9 @@ public async Task<ResultingGPOChanges> ReadGPOLocalGroups(string gpLink, string
140140
continue;
141141
}
142142

143-
if (!result.Value.TryGetProperty(LDAPProperties.GPCFileSYSPath, out var filePath)) {
143+
if (!result.Value.TryGetProperty(LDAPProperties.GPCFileSYSPath, out var filePath) ||
144+
// Filter out GPOs that are disabled or the computer configuration is disabled
145+
(result.Value.TryGetProperty(LDAPProperties.Flags, out var flags) && flags is "2" or "3")) {
144146
GpoActionCache.TryAdd(linkDn, actions);
145147
continue;
146148
}

src/CommonLib/Processors/LdapPropertyProcessor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ public static Dictionary<string, object> ReadGPOProperties(IDirectoryObject entr
165165
var props = GetCommonProps(entry);
166166
entry.TryGetProperty(LDAPProperties.GPCFileSYSPath, out var path);
167167
props.Add("gpcpath", path.ToUpper());
168+
entry.TryGetProperty(LDAPProperties.Flags, out var flags);
169+
props.Add("gpostatus", flags);
168170
return props;
169171
}
170172

test/unit/GPOLocalGroupProcessorTest.cs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,146 @@ public async Task GPOLocalGroupProcessor_ReadGPOLocalGroups_Null_Gpcfilesyspath(
155155
Assert.Equal("teapot", actual.ObjectIdentifier);
156156
}
157157

158+
[Fact]
159+
public async Task GPOLocalGroupProcessor_ReadGPOLocalGroups_Does_Not_Skip_Enabled_And_Skips_Disabled_GPOs() {
160+
// Setup
161+
var mockLDAPUtils = new Mock<ILdapUtils>(MockBehavior.Loose);
162+
var gpcFileSysPath = Path.GetTempPath();
163+
164+
var groupsXmlPath = Path.Join(gpcFileSysPath, "MACHINE", "Preferences", "Groups", "Groups.xml");
165+
Directory.CreateDirectory(Path.GetDirectoryName(groupsXmlPath));
166+
await File.WriteAllTextAsync(groupsXmlPath, GroupXmlContent);
167+
168+
var gptTmplPath = Path.Join(gpcFileSysPath, "MACHINE", "Microsoft", "Windows NT", "SecEdit", "GptTmpl.inf");
169+
Directory.CreateDirectory(Path.GetDirectoryName(gptTmplPath));
170+
await File.WriteAllTextAsync(gptTmplPath, GpttmplInfContent);
171+
172+
var mockComputerEntry = new Mock<IDirectoryObject>();
173+
var mockSearchResultEntry = new Mock<IDirectoryObject>();
174+
var sid = "teapot";
175+
mockSearchResultEntry.Setup(x => x.TryGetSecurityIdentifier(out sid)).Returns(true);
176+
var mockResult = LdapResult<IDirectoryObject>.Ok(mockSearchResultEntry.Object);
177+
var mockSearchResults = new List<LdapResult<IDirectoryObject>> { mockResult };
178+
mockLDAPUtils
179+
.Setup(x => x.Query(
180+
It.Is<LdapQueryParameters>(y =>
181+
y.LDAPFilter.Equals(new LdapFilter().AddComputersNoMSAs().GetFilter()) &&
182+
y.Attributes.Equals(CommonProperties.ObjectSID)),
183+
It.IsAny<CancellationToken>())).Returns(mockSearchResults.ToAsyncEnumerable);
184+
mockComputerEntry.Setup(x => x.TryGetSecurityIdentifier(out sid)).Returns(true);
185+
mockLDAPUtils.Setup(x => x.ResolveAccountName(It.IsAny<string>(), It.IsAny<string>()))
186+
.ReturnsAsync((true, new TypedPrincipal("S-1-5-21-3130019616-2776909439-2417379446-513", Label.User)));
187+
188+
// Enabled
189+
var mockDirectory0 = new MockDirectoryObject("CN=Users,DC=testlab,DC=local", null, "",
190+
"ECAD920E-8EB1-4E31-A80E-DD36367F81F4");
191+
mockDirectory0.Properties = new Dictionary<string, string>() {
192+
{ LDAPProperties.GPCFileSYSPath, gpcFileSysPath },
193+
{ LDAPProperties.Flags, "0"}
194+
};
195+
var result0 = new List<LdapResult<IDirectoryObject>> {
196+
LdapResult<IDirectoryObject>.Ok(mockDirectory0),
197+
};
198+
// User Configuration Disabled
199+
var mockDirectory1 = new MockDirectoryObject("CN=Users,DC=testlab,DC=local", null, "",
200+
"ECAD920E-8EB1-4E31-A80E-DD36367F81F4");
201+
mockDirectory1.Properties = new Dictionary<string, string>() {
202+
{ LDAPProperties.GPCFileSYSPath, gpcFileSysPath },
203+
{ LDAPProperties.Flags, "1"}
204+
};
205+
var result1 = new List<LdapResult<IDirectoryObject>> {
206+
LdapResult<IDirectoryObject>.Ok(mockDirectory1),
207+
};
208+
// Computer Configuration Disabled -- Skipped
209+
var mockDirectory2 = new MockDirectoryObject("CN=Users,DC=testlab,DC=local", null, "",
210+
"ECAD920E-8EB1-4E31-A80E-DD36367F81F4");
211+
mockDirectory2.Properties = new Dictionary<string, string>() {
212+
{ LDAPProperties.GPCFileSYSPath, gpcFileSysPath },
213+
{ LDAPProperties.Flags, "2"}
214+
};
215+
var result2 = new List<LdapResult<IDirectoryObject>> {
216+
LdapResult<IDirectoryObject>.Ok(mockDirectory2),
217+
};
218+
// Disabled -- Skipped
219+
var mockDirectory3 = new MockDirectoryObject("CN=Users,DC=testlab,DC=local", null, "",
220+
"ECAD920E-8EB1-4E31-A80E-DD36367F81F4");
221+
mockDirectory3.Properties = new Dictionary<string, string>() {
222+
{ LDAPProperties.GPCFileSYSPath, gpcFileSysPath },
223+
{ LDAPProperties.Flags, "3"}
224+
};
225+
var result3 = new List<LdapResult<IDirectoryObject>> {
226+
LdapResult<IDirectoryObject>.Ok(mockDirectory3),
227+
};
228+
229+
mockLDAPUtils
230+
.Setup(x => x.Query(
231+
It.Is<LdapQueryParameters>(y =>
232+
y.LDAPFilter.Equals(new LdapFilter().AddAllObjects().GetFilter()) &&
233+
y.SearchScope.Equals(SearchScope.Base) &&
234+
y.Attributes.Contains(LDAPProperties.GPCFileSYSPath) &&
235+
y.Attributes.Contains(LDAPProperties.Flags) &&
236+
y.SearchBase.Equals("cn=foouser (blah)123/dc=somedomain", StringComparison.OrdinalIgnoreCase) &&
237+
y.DomainName.Equals("somedomain", StringComparison.OrdinalIgnoreCase)),
238+
It.IsAny<CancellationToken>()))
239+
.Returns(result0.ToAsyncEnumerable);
240+
mockLDAPUtils
241+
.Setup(x => x.Query(
242+
It.Is<LdapQueryParameters>(y =>
243+
y.LDAPFilter.Equals(new LdapFilter().AddAllObjects().GetFilter()) &&
244+
y.SearchScope.Equals(SearchScope.Base) &&
245+
y.Attributes.Contains(LDAPProperties.GPCFileSYSPath) &&
246+
y.Attributes.Contains(LDAPProperties.Flags) &&
247+
y.SearchBase.Equals("cn=foouser (blah)123/dc=someotherdomain", StringComparison.OrdinalIgnoreCase) &&
248+
y.DomainName.Equals("someotherdomain", StringComparison.OrdinalIgnoreCase)),
249+
It.IsAny<CancellationToken>()))
250+
.Returns(result1.ToAsyncEnumerable);
251+
mockLDAPUtils
252+
.Setup(x => x.Query(
253+
It.Is<LdapQueryParameters>(y =>
254+
y.LDAPFilter.Equals(new LdapFilter().AddAllObjects().GetFilter()) &&
255+
y.SearchScope.Equals(SearchScope.Base) &&
256+
y.Attributes.Contains(LDAPProperties.GPCFileSYSPath) &&
257+
y.Attributes.Contains(LDAPProperties.Flags) &&
258+
y.SearchBase.Equals("cn=foouser (blah)123/dc=somethirddomain", StringComparison.OrdinalIgnoreCase) &&
259+
y.DomainName.Equals("somethirddomain", StringComparison.OrdinalIgnoreCase)),
260+
It.IsAny<CancellationToken>()))
261+
.Returns(result2.ToAsyncEnumerable);
262+
mockLDAPUtils
263+
.Setup(x => x.Query(
264+
It.Is<LdapQueryParameters>(y =>
265+
y.LDAPFilter.Equals(new LdapFilter().AddAllObjects().GetFilter()) &&
266+
y.SearchScope.Equals(SearchScope.Base) &&
267+
y.Attributes.Contains(LDAPProperties.GPCFileSYSPath) &&
268+
y.Attributes.Contains(LDAPProperties.Flags) &&
269+
y.SearchBase.Equals("cn=foouser (blah)123/dc=somefourthdomain", StringComparison.OrdinalIgnoreCase) &&
270+
y.DomainName.Equals("somefourthdomain", StringComparison.OrdinalIgnoreCase)),
271+
It.IsAny<CancellationToken>()))
272+
.Returns(result3.ToAsyncEnumerable);
273+
274+
var processor = new GPOLocalGroupProcessor(mockLDAPUtils.Object);
275+
var testGPLinkProperty0 = "[LDAP:/o=foo/ou=foo Group (ABC123)/cn=foouser (blah)123/dc=somedomain;0;]";
276+
var testGPLinkProperty1 = "[LDAP:/o=foo/ou=foo Group (ABC123)/cn=foouser (blah)123/dc=someotherdomain;0;]";
277+
var testGPLinkProperty2 = "[LDAP:/o=foo/ou=foo Group (ABC123)/cn=foouser (blah)123/dc=somethirddomain;0;]";
278+
var testGPLinkProperty3 = "[LDAP:/o=foo/ou=foo Group (ABC123)/cn=foouser (blah)123/dc=somefourthdomain;0;]";
279+
280+
// Act
281+
var act0 = await processor.ReadGPOLocalGroups(testGPLinkProperty0, "DC=Testlab,DC=Local");
282+
var act1 = await processor.ReadGPOLocalGroups(testGPLinkProperty1, "DC=Testlab,DC=Local");
283+
var act2 = await processor.ReadGPOLocalGroups(testGPLinkProperty2, "DC=Testlab,DC=Local");
284+
var act3 = await processor.ReadGPOLocalGroups(testGPLinkProperty3, "DC=Testlab,DC=Local");
285+
286+
// Assert
287+
Assert.Single(act0.AffectedComputers);
288+
Assert.Single(act1.AffectedComputers);
289+
Assert.Single(act2.AffectedComputers);
290+
Assert.Single(act3.AffectedComputers);
291+
292+
Assert.Single(act0.LocalAdmins);
293+
Assert.Single(act1.LocalAdmins);
294+
Assert.Empty(act2.LocalAdmins);
295+
Assert.Empty(act3.LocalAdmins);
296+
}
297+
158298
[WindowsOnlyFact]
159299
public async Task GPOLocalGroupProcessor_ReadGPOLocalGroups() {
160300
var mockLDAPUtils = new Mock<ILdapUtils>(MockBehavior.Loose);

0 commit comments

Comments
 (0)