Skip to content

Commit 8a856ca

Browse files
committed
Better error handling for connection issues, better formatting, ability to edit the attributes list for the Explorer / Search pages, and bugfixes.
1 parent 1914ac0 commit 8a856ca

13 files changed

Lines changed: 431 additions & 110 deletions

File tree

pkg/ldaputils/actions.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,10 @@ func (lc *LDAPConn) KerbBindWithCCache(ccachePath string, server string, krbDoma
180180

181181
// Search
182182
func (lc *LDAPConn) Query(baseDN string, searchFilter string, scope int, showDeleted bool) ([]*ldap.Entry, error) {
183+
return lc.QueryWithAttrs(baseDN, searchFilter, scope, showDeleted, []string{})
184+
}
185+
186+
func (lc *LDAPConn) QueryWithAttrs(baseDN string, searchFilter string, scope int, showDeleted bool, attrs []string) ([]*ldap.Entry, error) {
183187
var controls []ldap.Control = nil
184188
if showDeleted {
185189
controls = []ldap.Control{ldap.NewControlMicrosoftShowDeleted()}
@@ -189,7 +193,7 @@ func (lc *LDAPConn) Query(baseDN string, searchFilter string, scope int, showDel
189193
baseDN,
190194
scope, ldap.NeverDerefAliases, 0, 0, false,
191195
searchFilter,
192-
[]string{},
196+
attrs,
193197
controls,
194198
)
195199

pkg/ldaputils/formats.go

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,22 @@ func ParseUACFlags(uacInt int) []string {
238238
return uacFlagsList
239239
}
240240

241+
func parseBitFlags(v uint32, flagMap map[uint32]string) []string {
242+
keys := make([]uint32, 0, len(flagMap))
243+
for k := range flagMap {
244+
keys = append(keys, k)
245+
}
246+
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
247+
248+
var result []string
249+
for _, bit := range keys {
250+
if v&bit != 0 {
251+
result = append(result, flagMap[bit])
252+
}
253+
}
254+
return result
255+
}
256+
241257
func ParseSystemFlags(v uint32) []string {
242258
sysFlagKeys := make([]uint32, 0)
243259
for k := range SystemFlags {
@@ -300,27 +316,52 @@ func FormatLDAPAttribute(attr *ldap.EntryAttribute, timeFormat string, timeOffse
300316
}
301317

302318
/* Special parsing for bitset expansions */
303-
if attr.Name == "userAccountControl" || attr.Name == "systemFlags" {
304-
switch attr.Name {
305-
case "userAccountControl":
306-
uacInt, err := strconv.Atoi(attr.Values[0])
307-
if err == nil {
308-
formattedEntries = ParseUACFlags(uacInt)
319+
bitsetAttrs := map[string]func(string) []string{
320+
"userAccountControl": func(v string) []string {
321+
i, err := strconv.Atoi(v)
322+
if err != nil {
323+
return nil
309324
}
310-
case "systemFlags":
311-
intValue, err := strconv.ParseInt(attr.Values[0], 10, 64)
312-
if err == nil {
313-
formattedEntries = ParseSystemFlags(uint32(intValue))
325+
return ParseUACFlags(i)
326+
},
327+
"systemFlags": func(v string) []string {
328+
i, err := strconv.ParseInt(v, 10, 64)
329+
if err != nil {
330+
return nil
314331
}
315-
}
332+
return ParseSystemFlags(uint32(i))
333+
},
334+
"trustAttributes": func(v string) []string {
335+
i, err := strconv.ParseUint(v, 10, 32)
336+
if err != nil {
337+
return nil
338+
}
339+
return parseBitFlags(uint32(i), TrustAttributeFlags)
340+
},
341+
"pwdProperties": func(v string) []string {
342+
i, err := strconv.ParseUint(v, 10, 32)
343+
if err != nil {
344+
return nil
345+
}
346+
return parseBitFlags(uint32(i), PwdPropertiesFlags)
347+
},
348+
"searchFlags": func(v string) []string {
349+
i, err := strconv.ParseUint(v, 10, 32)
350+
if err != nil {
351+
return nil
352+
}
353+
return parseBitFlags(uint32(i), SearchFlagsMap)
354+
},
355+
}
316356

357+
if parseFn, ok := bitsetAttrs[attr.Name]; ok {
358+
formattedEntries = parseFn(attr.Values[0])
317359
for _, x := range formattedEntries {
318360
formattedAttrValues = append(formattedAttrValues, FormattedAttrValue{
319361
OriginalValue: attr.Values[0],
320362
FormattedValue: x,
321363
})
322364
}
323-
324365
return FormattedAttr{formattedAttrValues}
325366
}
326367

@@ -329,9 +370,9 @@ func FormatLDAPAttribute(attr *ldap.EntryAttribute, timeFormat string, timeOffse
329370
// Format the value
330371
var formattedEntry string
331372
switch attr.Name {
332-
case "objectSid":
373+
case "objectSid", "securityIdentifier":
333374
formattedEntry = "SID{" + ConvertSID(hex.EncodeToString(attr.ByteValues[idx])) + "}"
334-
case "objectGUID", "schemaIDGUID":
375+
case "objectGUID", "schemaIDGUID", "attributeSecurityGUID":
335376
formattedEntry = "GUID{" + ConvertGUID(hex.EncodeToString(attr.ByteValues[idx])) + "}"
336377
case "whenCreated", "whenChanged":
337378
formattedEntry = FormatLDAPTime(val, timeFormat, timeOffset)
@@ -371,7 +412,7 @@ func FormatLDAPAttribute(attr *ldap.EntryAttribute, timeFormat string, timeOffse
371412
if ok {
372413
formattedEntry = instanceType
373414
}
374-
case "logonHours", "dSASignature":
415+
case "logonHours", "dSASignature", "oMObjectClass", "cACertificate":
375416
formattedEntry = "HEX{" + hex.EncodeToString(attr.ByteValues[idx]) + "}"
376417
case "msDS-MaximumPasswordAge", "msDS-MinimumPasswordAge", "msDS-LockoutDuration", "msDS-LockoutObservationWindow", "lockoutDuration", "lockOutObservationWindow", "maxPwdAge", "minPwdAge", "forceLogoff", "msDS-UserTGTLifetime", "msDS-ComputerTGTLifetime", "msDS-ServiceTGTLifetime":
377418
duration, err := ParseMSDuration(val)

pkg/ldaputils/vars.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,51 @@ var InstanceTypeMap = map[int]string{
178178
32: "NamingContextRemovalFromDSA",
179179
}
180180

181+
// Reference:
182+
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/d2435927-0999-4c62-8c6d-13ba31a52e1a
183+
var TrustAttributeFlags = map[uint32]string{
184+
0x00000001: "NON_TRANSITIVE",
185+
0x00000002: "UPLEVEL_ONLY",
186+
0x00000004: "QUARANTINED_DOMAIN",
187+
0x00000008: "FOREST_TRANSITIVE",
188+
0x00000010: "CROSS_ORGANIZATION",
189+
0x00000020: "WITHIN_FOREST",
190+
0x00000040: "TREAT_AS_EXTERNAL",
191+
0x00000080: "USES_RC4_ENCRYPTION",
192+
0x00000200: "CROSS_ORGANIZATION_NO_TGT_DELEGATION",
193+
0x00000400: "PIM_TRUST",
194+
0x00000800: "CROSS_ORGANIZATION_ENABLE_TGT_DELEGATION",
195+
}
196+
197+
// Reference:
198+
// https://learn.microsoft.com/en-us/windows/win32/adschema/a-pwdproperties
199+
var PwdPropertiesFlags = map[uint32]string{
200+
0x00000001: "PASSWORD_COMPLEX",
201+
0x00000002: "PASSWORD_NO_ANON_CHANGE",
202+
0x00000004: "PASSWORD_NO_CLEAR_CHANGE",
203+
0x00000008: "LOCKOUT_ADMINS",
204+
0x00000010: "PASSWORD_STORE_CLEARTEXT",
205+
0x00000020: "REFUSE_PASSWORD_CHANGE",
206+
}
207+
208+
// Reference:
209+
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/d2435927-0999-4c62-8c6d-13ba31a52e1a
210+
var SearchFlagsMap = map[uint32]string{
211+
0x00000001: "fATTINDEX",
212+
0x00000002: "fPDNTATTINDEX",
213+
0x00000004: "fANR",
214+
0x00000008: "fPRESERVEONDELETE",
215+
0x00000010: "fCOPY",
216+
0x00000020: "fTUPLEINDEX",
217+
0x00000040: "fSUBTREEATTINDEX",
218+
0x00000080: "fCONFIDENTIAL",
219+
0x00000100: "fNEVERVALUEAUDIT",
220+
0x00000200: "fRODCFilteredAttribute",
221+
0x00000400: "fEXTENDEDLINKTRACKING",
222+
0x00000800: "fBASEONLY",
223+
0x00001000: "fPARTITIONSECRET",
224+
}
225+
181226
type LibQuery struct {
182227
Title string
183228
Filter string

tui/ace.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ func removeAce(aceIdx int) {
140140
}
141141
}
142142
} else {
143-
updateLog(fmt.Sprint(err), "red")
143+
handleLDAPError(err)
144144
}
145145
}
146146

@@ -248,7 +248,7 @@ func createOrUpdateAce(aceIdx int, newAllowOrDeny bool, newACEFlags int, newMask
248248
// Update selection
249249
selectDaclEntry(newACE)
250250
} else {
251-
updateLog(fmt.Sprint(err), "red")
251+
handleLDAPError(err)
252252
}
253253
}
254254

tui/dacl.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ func updateDaclEntries() {
365365

366366
daclEntriesPanel.Select(1, 1)
367367
} else {
368-
updateLog(fmt.Sprint(err), "red")
368+
handleLDAPError(err)
369369
}
370370
}
371371

@@ -465,7 +465,7 @@ func loadChangeOwnerForm() {
465465

466466
go app.QueueUpdateDraw(updateDaclEntries)
467467
} else {
468-
updateLog(fmt.Sprint(err), "red")
468+
handleLDAPError(err)
469469
}
470470
app.SetRoot(appPanel, true).SetFocus(daclEntriesPanel)
471471
})
@@ -541,7 +541,7 @@ func loadChangeControlFlagsForm() {
541541
updateLog("Control flags updated for '"+object+"'", "green")
542542
go app.QueueUpdateDraw(updateDaclEntries)
543543
} else {
544-
updateLog(fmt.Sprint(err), "red")
544+
handleLDAPError(err)
545545
}
546546

547547
app.SetRoot(appPanel, true).SetFocus(daclEntriesPanel)

tui/dns.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ func reloadADIDNSNode(currentNode *tview.TreeNode) {
219219
if err == nil {
220220
updateLog(fmt.Sprintf("Loaded node '%s'", node.DN), "green")
221221
} else {
222-
updateLog(fmt.Sprint(err), "red")
222+
handleLDAPError(err)
223223
}
224224

225225
showDetails(node.DN)
@@ -315,7 +315,7 @@ func loadZoneNodes(zoneNode *tview.TreeNode) int {
315315

316316
nodes, err := lc.GetADIDNSNodes(zoneDN)
317317
if err != nil {
318-
updateLog(fmt.Sprint(err), "red")
318+
handleLDAPError(err)
319319
return -1
320320
}
321321

@@ -649,8 +649,17 @@ func queryDnsZones(targetZone string) {
649649
app.QueueUpdateDraw(func() {
650650
updateLog("Querying ADIDNS zones...", "yellow")
651651

652-
domainZones, _ = lc.GetADIDNSZones(targetZone, false)
653-
forestZones, _ = lc.GetADIDNSZones(targetZone, true)
652+
var domainErr, forestErr error
653+
domainZones, domainErr = lc.GetADIDNSZones(targetZone, false)
654+
if domainErr != nil {
655+
handleLDAPError(domainErr)
656+
return
657+
}
658+
forestZones, forestErr = lc.GetADIDNSZones(targetZone, true)
659+
if forestErr != nil {
660+
handleLDAPError(forestErr)
661+
return
662+
}
654663

655664
totalZones := len(domainZones) + len(forestZones)
656665
if totalZones == 0 {

tui/dnsmodify.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func addZoneHandler(zoneForm *XForm, currentFocus tview.Primitive) func() {
4646
zoneDN, err := lc.AddADIDNSZone(zoneName, defaultProps, isForest)
4747

4848
if err != nil {
49-
updateLog(fmt.Sprint(err), "red")
49+
handleLDAPError(err)
5050
app.SetRoot(appPanel, true).SetFocus(currentFocus)
5151
return
5252
}
@@ -321,7 +321,7 @@ func openActionNodeForm(target *tview.TreeNode, update bool) {
321321
}
322322

323323
if err != nil {
324-
updateLog(fmt.Sprint(err), "red")
324+
handleLDAPError(err)
325325
app.SetRoot(appPanel, true).SetFocus(currentFocus)
326326
return
327327
}
@@ -492,7 +492,7 @@ func openDeleteRecordForm(record *tview.TreeNode) {
492492

493493
err = lc.ReplaceADIDNSRecords(nodeDN, updateRecords)
494494
if err != nil {
495-
updateLog(fmt.Sprint(err), "red")
495+
handleLDAPError(err)
496496
app.SetRoot(appPanel, true).SetFocus(currentFocus)
497497
return
498498
}

0 commit comments

Comments
 (0)