diff --git a/modules/analyze/webservicefuncs.go b/modules/analyze/webservicefuncs.go index da957f4..3c21954 100644 --- a/modules/analyze/webservicefuncs.go +++ b/modules/analyze/webservicefuncs.go @@ -105,7 +105,7 @@ func analysisfuncs(ws *webservice) { case "dn", "distinguishedname": o, found = ws.Objs.Find(activedirectory.DistinguishedName, engine.AttributeValueString(vars["id"])) case "sid": - sid, err := windowssecurity.SIDFromString(vars["id"]) + sid, err := windowssecurity.ParseStringSID(vars["id"]) if err != nil { w.WriteHeader(400) // bad request w.Write([]byte(err.Error())) @@ -147,11 +147,12 @@ func analysisfuncs(ws *webservice) { Attributes: make(map[string][]string), } - for attr, values := range o.AttributeValueMap() { + o.AttrIterator(func(attr engine.Attribute, values engine.AttributeValues) bool { slice := values.StringSlice() sort.StringSlice(slice).Sort() od.Attributes[attr.String()] = slice - } + return true + }) if r.FormValue("format") == "json" { w.WriteHeader(200) @@ -567,12 +568,13 @@ func analysisfuncs(ws *webservice) { `, id, node.Label(), node.DN()) if alldetails { - for attribute, values := range node.AttributeValueMap() { + node.AttrIterator(func(attribute engine.Attribute, values engine.AttributeValues) bool { valuesjoined := strings.Join(values.StringSlice(), ", ") if util.IsASCII(valuesjoined) { fmt.Fprintf(w, " %v %v\n", attribute, valuesjoined) } - } + return true + }) } fmt.Fprintf(w, " ]\n") } @@ -601,7 +603,7 @@ func analysisfuncs(ws *webservice) { } if alldetails { - for attribute, values := range object.AttributeValueMap() { + object.AttrIterator(func(attribute engine.Attribute, values engine.AttributeValues) bool { if values != nil { valuesjoined := strings.Join(values.StringSlice(), ", ") if util.IsASCII(valuesjoined) { @@ -611,7 +613,8 @@ func analysisfuncs(ws *webservice) { }) } } - } + return true + }) } graph.Nodes = append(graph.Nodes, xmlnode) } diff --git a/modules/engine/analyzeobjects.go b/modules/engine/analyzeobjects.go index ece9f1f..6038eb5 100644 --- a/modules/engine/analyzeobjects.go +++ b/modules/engine/analyzeobjects.go @@ -6,7 +6,7 @@ import ( "github.com/lkarlslund/adalanche/modules/ui" ) -var EdgeMemberOfGroup = NewEdge("MemberOfGroup") // FIXME, this should be generalized to expand-anyway-priority somehoe +var EdgeMemberOfGroup = NewEdge("MemberOfGroup") var SortBy Attribute = NonExistingAttribute diff --git a/modules/engine/attributes.go b/modules/engine/attributes.go index dc9f265..8eee20a 100644 --- a/modules/engine/attributes.go +++ b/modules/engine/attributes.go @@ -63,7 +63,7 @@ var ( ObjectSid = NewAttribute("objectSid").Single() // Strange yes, but in the final results there are multiple objects with the same SID ObjectGUID = NewAttribute("objectGUID").Single().Unique() NTSecurityDescriptor = NewAttribute("nTSecurityDescriptor").Single() - SchemaIDGUID = NewAttribute("schemaIDGUID") // Dirty, needs proper FIXME for multi domain + SchemaIDGUID = NewAttribute("schemaIDGUID") RightsGUID = NewAttribute("rightsGUID") AttributeSecurityGUID = NewAttribute("attributeSecurityGUID") diff --git a/modules/engine/attributevalue.go b/modules/engine/attributevalue.go index f916c15..b9b8a48 100644 --- a/modules/engine/attributevalue.go +++ b/modules/engine/attributevalue.go @@ -13,46 +13,44 @@ import ( ) func CompareAttributeValues(a, b AttributeValue) bool { - araw := a.Raw() - braw := b.Raw() - switch na := araw.(type) { - case bool: - nb, btype := braw.(bool) + switch na := a.(type) { + case AttributeValueBool: + nb, btype := b.(AttributeValueBool) if btype { return na == nb } - case string: - nb, btype := braw.(string) + case AttributeValueString: + nb, btype := b.(AttributeValueString) if btype { - return strings.EqualFold(na, nb) + return strings.EqualFold(string(na), string(nb)) } - case int64: - nb, btype := braw.(int64) + case AttributeValueInt: + nb, btype := b.(AttributeValueInt) if btype { return na == nb } - case time.Time: - nb, btype := braw.(time.Time) + case AttributeValueTime: + nb, btype := b.(AttributeValueTime) if btype { - return na.Equal(nb) + return time.Time(na).Equal(time.Time(nb)) } - case []byte: - nb, btype := braw.([]byte) + case AttributeValueBlob: + nb, btype := b.(AttributeValueBlob) if btype { - return bytes.Equal(na, nb) + return bytes.Equal([]byte(na), []byte(nb)) } - case windowssecurity.SID: - nb, btype := braw.(windowssecurity.SID) + case AttributeValueSID: + nb, btype := b.(AttributeValueSID) if btype { return string(na) == string(nb) } - case uuid.UUID: - nb, btype := braw.(uuid.UUID) + case AttributeValueGUID: + nb, btype := b.(AttributeValueGUID) if btype { return na == nb } - case *Object: - nb, btype := braw.(*Object) + case AttributeValueObject: + nb, btype := b.(AttributeValueObject) if btype { return na == nb // Exact same object pointed to in memory } @@ -171,6 +169,7 @@ type AttributeValue interface { String() string Raw() interface{} IsZero() bool + // Compare(other AttributeValue) bool } type AttributeValueObject struct { @@ -189,7 +188,7 @@ func (avo AttributeValueObject) IsZero() bool { if avo.Object == nil { return true } - return len(avo.values) == 0 + return avo.values.Len() == 0 } type AttributeValueString string diff --git a/modules/engine/attributevaluemap.go b/modules/engine/attributevaluemap.go index 42e83fe..9eeb10c 100644 --- a/modules/engine/attributevaluemap.go +++ b/modules/engine/attributevaluemap.go @@ -1,20 +1,77 @@ package engine -type AttributeValueMap map[Attribute]AttributeValues - -func NewAttributeValueMap() AttributeValueMap { - return make(AttributeValueMap) +type AttributeValueMap struct { + m map[Attribute]AttributeValues + // firstattribute Attribute + // data []AttributeValues } func (avm AttributeValueMap) Get(a Attribute) (av AttributeValues, found bool) { - av, found = avm[a] + if avm.m == nil { + return nil, false + } + av, found = avm.m[a] return + // if a < avm.firstattribute || int(a-avm.firstattribute) >= len(avm.data) { + // return nil, false + // } + // result := avm.data[a-avm.firstattribute] + // return result, result != nil +} + +func (avm *AttributeValueMap) Set(a Attribute, av AttributeValues) { + if avm.m == nil { + avm.m = make(map[Attribute]AttributeValues) + } + avm.m[a] = av + // if len(avm.data) == 0 { + // avm.firstattribute = a + // avm.data = make([]AttributeValues, 1) + // avm.data[0] = av + // } else if a < avm.firstattribute { + // shift := int(avm.firstattribute - a) + // newdata := make([]AttributeValues, len(avm.data)+shift, len(avm.data)+shift) + // copy(newdata[shift:], avm.data) + // avm.data = newdata + // avm.firstattribute = a + // } else if int(a-avm.firstattribute) >= len(avm.data) { + // add := int(a-avm.firstattribute) - len(avm.data) + 1 + // newdata := make([]AttributeValues, len(avm.data)+add, len(avm.data)+add) + // copy(newdata, avm.data) + // avm.data = newdata + // } + // avm.data[a-avm.firstattribute] = av +} + +func (avm AttributeValueMap) Len() int { + return len(avm.m) + // var count int + // for _, v := range avm.data { + // if v != nil { + // count++ + // } + // } + // return count } -func (avm AttributeValueMap) Set(a Attribute, av AttributeValues) { - avm[a] = av +func (avm *AttributeValueMap) Clear(a Attribute) { + if avm.m != nil { + delete(avm.m, a) + } + // avm.data[a-avm.firstattribute] = nil } -func (avm AttributeValueMap) Clear(a Attribute) { - delete(avm, a) +func (avm AttributeValueMap) Iterate(f func(attr Attribute, values AttributeValues) bool) { + for attr, values := range avm.m { + if !f(attr, values) { + break + } + } + // for i, values := range avm.data { + // if values != nil { + // if !f(avm.firstattribute+Attribute(i), values) { + // break + // } + // } + // } } diff --git a/modules/engine/edge.go b/modules/engine/edge.go index a8b4f4b..00470b7 100644 --- a/modules/engine/edge.go +++ b/modules/engine/edge.go @@ -15,7 +15,7 @@ type EdgeAnalyzer struct { // Increas this when we run out of space const PMBSIZE = 2 -const MAXPWNMETHODPOSSIBLE = PMBSIZE * 64 +const MAXEDGEPOSSIBLE = PMBSIZE * 64 type EdgeBitmap [PMBSIZE]uint64 type Probability int8 @@ -147,7 +147,7 @@ func NewEdge(name string) Edge { } newindex := Edge(len(edgeInfos)) - if newindex == MAXPWNMETHODPOSSIBLE { + if newindex == MAXEDGEPOSSIBLE { panic("Too many Edge definitions") } @@ -232,22 +232,14 @@ var ( var AllEdgesBitmap EdgeBitmap -var EdgePopularity [MAXPWNMETHODPOSSIBLE]uint64 +var EdgePopularity [MAXEDGEPOSSIBLE]uint64 func init() { - for i := Edge(0); i < MAXPWNMETHODPOSSIBLE; i++ { + for i := Edge(0); i < MAXEDGEPOSSIBLE; i++ { AllEdgesBitmap = AllEdgesBitmap.set(i) } } -/* -type PwnMethodsAndProbabilities struct { - EdgeBitmap // Indicates if we have this method registered - probabilitymap EdgeBitmap // Indicates if we have a probability set or should just return 100 - probabilities Probabilities -} -*/ - type EdgeDirection int const ( @@ -255,7 +247,7 @@ const ( In EdgeDirection = 1 ) -type EdgeConnections map[*Object]EdgeBitmap //sAndProbabilities +type EdgeConnections map[*Object]EdgeBitmap var globalEdgeConnectionsLock sync.Mutex // Ugly but it will do diff --git a/modules/engine/object.go b/modules/engine/object.go index ecd4417..a657590 100644 --- a/modules/engine/object.go +++ b/modules/engine/object.go @@ -55,15 +55,6 @@ type Object struct { sid windowssecurity.SID children []*Object - members map[*Object]struct{} - membersrecursive map[*Object]struct{} - - memberof map[*Object]struct{} - memberofrecursive map[*Object]struct{} - - memberofsid []windowssecurity.SID - memberofsidrecursive []windowssecurity.SID - id uint32 guid uuid.UUID // objectcategoryguid uuid.UUID @@ -143,7 +134,7 @@ func (o *Object) AbsorbEx(source *Object, fast bool) { // Fast mode does not merge values, it just relinks the source to the target if !fast { - for attr, values := range source.values { + source.AttrIterator(func(attr Attribute, values AttributeValues) bool { var val AttributeValues tval := target.attr(attr) sval := values @@ -153,8 +144,8 @@ func (o *Object) AbsorbEx(source *Object, fast bool) { } else if sval.Len() == 0 { panic(fmt.Sprintf("Attribute %v with ZERO LENGTH data failure", attr.String())) } else if tval.Len() == 1 && sval.Len() == 1 { - tvalue := tval.Slice()[0] - svalue := sval.Slice()[0] + tvalue := tval.First() + svalue := sval.First() if CompareAttributeValues(tvalue, svalue) { val = tval // They're the same, so pick any @@ -164,29 +155,34 @@ func (o *Object) AbsorbEx(source *Object, fast bool) { } } else { // One or more of them have more than one value, do it the hard way - tvalslice := tval.Slice() - svalslice := sval.Slice() - - resultingvalues := make([]AttributeValue, tval.Len()) - copy(resultingvalues, tvalslice) - - for _, svalue := range svalslice { - var alreadythere bool - compareloop: - for _, tvalue := range tvalslice { - if CompareAttributeValues(svalue, tvalue) { // Crap!! - alreadythere = true - break compareloop + var destinationSlice AttributeValueSlice + var testvalues AttributeValues + + if tval.Len() > sval.Len() { + destinationSlice = tval.Slice() + testvalues = sval + } else { + destinationSlice = sval.Slice() + testvalues = tval + } + + resultingvalues := destinationSlice + + testvalues.Iterate(func(testvalue AttributeValue) bool { + for _, existingvalue := range destinationSlice { + if CompareAttributeValues(existingvalue, testvalue) { // Crap!! + return false } } - if !alreadythere { - resultingvalues = append(resultingvalues, svalue) - } - } - val = AttributeValueSlice(resultingvalues) + resultingvalues = append(resultingvalues, testvalue) + return true + }) + + val = resultingvalues } target.set(attr, val) - } + return true + }) } for pwntarget, methods := range source.edges[Out] { @@ -205,38 +201,6 @@ func (o *Object) AbsorbEx(source *Object, fast bool) { delete(pwner.edges[Out], source) } - // For everyone that is a member of source, relink them to the target - if target.members == nil { - target.members = make(map[*Object]struct{}) - } - for newmember := range source.members { - target.members[newmember] = struct{}{} - // delete(source.members, newmember) // Handled below by setting to nil - - // Relink the memberof attribute of the member and clear cache - delete(newmember.memberof, source) - newmember.memberof[target] = struct{}{} - newmember.memberofrecursive = nil - } - source.members = nil - source.membersrecursive = nil - - if target.memberof == nil { - target.memberof = make(map[*Object]struct{}) - } - - for newmemberof := range source.memberof { - target.memberof[newmemberof] = struct{}{} - // delete(source.memberof, newmemberof) // handled below by setting to nil - - // Relink the member attribute of the newmemberof - newmemberof.members[target] = struct{}{} - delete(newmemberof.members, source) - newmemberof.membersrecursive = nil - } - source.memberof = nil - source.memberofrecursive = nil - for _, child := range source.children { target.adopt(child) } @@ -265,26 +229,20 @@ func (o *Object) AbsorbEx(source *Object, fast bool) { target.objecttype = 0 // Recalculate this - target.memberofrecursive = nil // Clear cache - target.membersrecursive = nil // Clear cache - - target.memberofsid = nil // Clear cache - target.memberofsidrecursive = nil // Clear cache - source.invalidated = true // Invalid object } -func (o *Object) AttributeValueMap() AttributeValueMap { - o.lock() - defer o.unlock() - val := o.values - for attr, _ := range val { - if attributenums[attr].onget != nil { - val[attr], _ = attributenums[attr].onget(o, attr) - } - } - return val -} +// func (o *Object) AttributeValueMap() AttributeValueMap { +// o.lock() +// defer o.unlock() +// val := o.values +// for attr, _ := range val { +// if attributenums[attr].onget != nil { +// val[attr], _ = attributenums[attr].onget(o, attr) +// } +// } +// return val +// } type StringMap map[string][]string @@ -316,9 +274,10 @@ func (o *Object) NameStringMap() StringMap { o.lock() defer o.unlock() result := make(StringMap) - for attr, values := range o.values { + o.values.Iterate(func(attr Attribute, values AttributeValues) bool { result[attr.String()] = values.StringSlice() - } + return true + }) return result } @@ -383,20 +342,16 @@ func (o *Object) Type() ObjectType { return o.objecttype } - category := o.OneAttrString(ObjectCategory) - if category != "" { - equalpos := strings.Index(category, "=") - commapos := strings.Index(category, ",") - if equalpos == -1 || commapos == -1 || equalpos >= commapos { - // Just keep it as-is - } else { - category = category[equalpos+1 : commapos] - } - } else { - category = o.OneAttrString(ObjectCategorySimple) + category := o.Attr(ObjectCategorySimple) + if category.Len() == 0 { + category = o.AttrRendered(ObjectCategory) } - objecttype, found := ObjectTypeLookup(category) + if category.Len() == 0 { + return ObjectTypeOther + } + + objecttype, found := ObjectTypeLookup(category.First().String()) if found { o.objecttype = objecttype } @@ -484,15 +439,10 @@ func (o *Object) OneAttrString(attr Attribute) string { if !found { return "" } - if ao, ok := a.(AttributeValueOne); ok { - return ao.Value.String() - } - if a.Len() == 1 { - ui.Warn().Msgf("Inefficient attribute storage for %v - multival used for one value ...", attr.String()) - return a.Slice()[0].String() + if a.Len() != 1 { + ui.Error().Msgf("Attribute %v lookup for ONE value, but contains %v (%v)", attr.String(), a.Len(), strings.Join(a.StringSlice(), ", ")) } - ui.Error().Msgf("Attribute %v lookup for ONE value, but contains %v (%v)", attr.String(), a.Len(), strings.Join(a.StringSlice(), ", ")) - return "" + return a.First().String() } func (o *Object) OneAttrRaw(attr Attribute) interface{} { @@ -523,12 +473,15 @@ func (o *Object) HasAttr(attr Attribute) bool { } func (o *Object) HasAttrValue(attr Attribute, hasvalue AttributeValue) bool { - for _, value := range o.Attr(attr).Slice() { + var result bool + o.Attr(attr).Iterate(func(value AttributeValue) bool { if CompareAttributeValues(value, hasvalue) { - return true + result = true + return false } - } - return false + return true + }) + return result } func (o *Object) AttrInt(attr Attribute) (int64, bool) { @@ -563,139 +516,6 @@ func (o *Object) AttrTimestamp(attr Attribute) (time.Time, bool) { // FIXME, swi return t, true } -func (o *Object) AddMember(member *Object) { - member.lock() - if _, found := member.memberof[o]; found { - member.unlock() - return - } - if member.memberof == nil { - member.memberof = make(map[*Object]struct{}) - } - member.memberof[o] = struct{}{} - member.unlock() - o.lock() - if o.members == nil { - o.members = make(map[*Object]struct{}) - } - o.members[member] = struct{}{} - o.unlock() -} - -func (o *Object) Members(recursive bool) []*Object { - o.lock() - defer o.unlock() - if !recursive { - return util.KeysToSlice(o.members) - } - - members := make(map[*Object]struct{}) - o.recursemembers(&members) - - membersarray := make([]*Object, len(members)) - var i int - for member := range members { - membersarray[i] = member - i++ - } - return membersarray -} - -func (o *Object) recursemembers(members *map[*Object]struct{}) { - for directmember := range o.members { - if _, found := (*members)[directmember]; found { - // endless loop, not today thanks - continue - } - (*members)[directmember] = struct{}{} - directmember.recursemembers(members) - } -} - -func (o *Object) MemberOf(recursive bool) []*Object { - o.lock() - defer o.unlock() - return o.memberofr(recursive) -} - -func (o *Object) memberofr(recursive bool) []*Object { - if !recursive || len(o.memberof) == 0 { - return util.KeysToSlice(o.memberof) - } - - if o.memberofrecursive != nil { - return util.KeysToSlice(o.memberofrecursive) - } - - memberof := make(map[*Object]struct{}) - o.recursememberof(&memberof) - - o.memberofrecursive = memberof - return util.KeysToSlice(memberof) -} - -// Recursive memberof, returns true if loop is detected -func (o *Object) recursememberof(memberof *map[*Object]struct{}) bool { - var loop bool - for directmemberof := range o.memberof { - if _, found := (*memberof)[directmemberof]; found { - // endless loop, not today thanks - loop = true - continue - } - (*memberof)[directmemberof] = struct{}{} - if directmemberof.recursememberof(memberof) { - loop = true - } - } - return loop -} - -func (o *Object) MemberOfSID(recursive bool) []windowssecurity.SID { - o.rlock() - if !recursive { - mos := o.memberofsid - if mos == nil { - o.runlock() - memberofsid := make([]windowssecurity.SID, len(o.memberof)) - i := 0 - for memberof := range o.memberof { - memberofsid[i] = memberof.SID() - i++ - } - o.lock() - o.memberofsid = memberofsid - o.unlock() - - return memberofsid - } - - o.runlock() - return mos - } - - mosr := o.memberofsidrecursive - if mosr != nil { - o.runlock() - return mosr - } - - memberofrecursive := o.memberofr(true) - o.runlock() - - mosr = make([]windowssecurity.SID, len(memberofrecursive)) - - for i, memberof := range memberofrecursive { - mosr[i] = memberof.SID() - } - - o.lock() - o.memberofsidrecursive = mosr - o.unlock() - - return mosr -} - // Wrapper for Set - easier to call func (o *Object) SetValues(a Attribute, values ...AttributeValue) { if values == nil { @@ -857,7 +677,8 @@ func (o *Object) setFlex(flexinit ...interface{}) { o.set(attribute, newdata) } } - avsPool.Put(data[:0]) + data = data[:0] + avsPool.Put(data) } func (o *Object) Set(a Attribute, values AttributeValues) { @@ -958,19 +779,17 @@ func (o *Object) set(a Attribute, values AttributeValues) { func (o *Object) Meta() map[string]string { result := make(map[string]string) - for attr, value := range o.values { + o.AttrIterator(func(attr Attribute, value AttributeValues) bool { if attr.String()[0] == '_' { - result[attr.String()] = value.Slice()[0].String() + result[attr.String()] = value.First().String() } - } + return true + }) return result } func (o *Object) init() { o.id = atomic.AddUint32(&idcounter, 1) - if o.values == nil { - o.values = NewAttributeValueMap() - } if o.edges[Out] == nil || o.edges[In] == nil { o.edges[Out] = make(EdgeConnections) o.edges[In] = make(EdgeConnections) @@ -980,9 +799,9 @@ func (o *Object) init() { func (o *Object) String() string { var result string result += "OBJECT " + o.DN() + "\n" - for attr, values := range o.AttributeValueMap() { + o.AttrIterator(func(attr Attribute, values AttributeValues) bool { if attr == NTSecurityDescriptor { - continue + return true // continue } result += " " + attributenums[attr].name + ":\n" for _, value := range values.Slice() { @@ -998,7 +817,8 @@ func (o *Object) String() string { // dump with recursion - fixme an.Children() } - } + return true // one more + }) return result } @@ -1017,9 +837,10 @@ func (o *Object) StringACL(ao *Objects) string { // Dump the object to simple map type for debugging func (o *Object) ValueMap() map[string][]string { result := make(map[string][]string) - for attr, values := range o.values { + o.AttrIterator(func(attr Attribute, values AttributeValues) bool { result[attr.String()] = values.StringSlice() - } + return true + }) return result } @@ -1068,7 +889,7 @@ func (o *Object) SID() windowssecurity.SID { o.sidcached = true if asid, ok := o.get(ObjectSid); ok { if asid.Len() == 1 { - if sid, ok := asid.Slice()[0].Raw().(windowssecurity.SID); ok { + if sid, ok := asid.First().Raw().(windowssecurity.SID); ok { o.sid = sid } } @@ -1086,7 +907,7 @@ func (o *Object) GUID() uuid.UUID { o.guidcached = true if aguid, ok := o.get(ObjectGUID); ok { if aguid.Len() == 1 { - if guid, ok := aguid.Slice()[0].Raw().(uuid.UUID); ok { + if guid, ok := aguid.First().Raw().(uuid.UUID); ok { o.guid = guid } } @@ -1126,7 +947,7 @@ func (o *Object) EdgeToEx(target *Object, method Edge, force bool) { } tsid := target.SID() - if osid != windowssecurity.BlankSID && osid == tsid { + if !osid.IsBlank() && osid == tsid { return } } @@ -1175,6 +996,10 @@ func (o *Object) EdgeIterator(direction EdgeDirection, ei func(target *Object, e o.unlock() } +func (o *Object) AttrIterator(f func(attr Attribute, avs AttributeValues) bool) { + o.values.Iterate(f) +} + func (o *Object) ChildOf(parent *Object) { o.lock() if o.parent != nil { diff --git a/modules/engine/objects.go b/modules/engine/objects.go index 1ccf8bf..77b93dc 100644 --- a/modules/engine/objects.go +++ b/modules/engine/objects.go @@ -286,7 +286,7 @@ func (os *Objects) ReindexObject(o *Object, isnew bool) { for i, index := range os.indexes { attribute := Attribute(i) if index != nil { - for _, value := range o.AttrRendered(attribute).Slice() { + o.AttrRendered(attribute).Iterate(func(value AttributeValue) bool { // If it's a string, lowercase it before adding to index, we do the same on lookups indexval := AttributeValueToIndex(value) @@ -298,12 +298,13 @@ func (os *Objects) ReindexObject(o *Object, isnew bool) { ui.Warn().Msgf("Duplicate index %v value %v when trying to add %v, already exists as %v, index still points to original object", attribute.String(), value.String(), o.Label(), existing[0].Label()) ui.Debug().Msgf("NEW DN: %v", o.DN()) ui.Debug().Msgf("EXISTING DN: %v", existing[0].DN()) - continue + return true } } index.Add(indexval, o, !isnew) - } + return true + }) } } @@ -383,16 +384,15 @@ func (os *Objects) Merge(attrtomerge []Attribute, o *Object) bool { os.objectmutex.RUnlock() // var deb int + var merged bool if len(attrtomerge) > 0 { for _, mergeattr := range attrtomerge { - if !o.HasAttr(mergeattr) { - continue - } - for _, lookfor := range o.Attr(mergeattr).Slice() { - if mergetargets, found := os.FindMultiOrAdd(mergeattr, lookfor, nil); found { + o.Attr(mergeattr).Iterate(func(lookfor AttributeValue) bool { + if mergetargets, found := os.FindMulti(mergeattr, lookfor); found { targetloop: for _, mergetarget := range mergetargets { - for attr, values := range o.AttributeValueMap() { + var failed bool + o.AttrIterator(func(attr Attribute, values AttributeValues) bool { if attr.IsSingle() && mergetarget.HasAttr(attr) { if !CompareAttributeValues(values.First(), mergetarget.Attr(attr).First()) { // Conflicting attribute values, we can't merge these @@ -401,9 +401,14 @@ func (os *Objects) Merge(attrtomerge []Attribute, o *Object) bool { // ui.Debug().Msgf("Object details: %v", o.StringNoACL()) // ui.Debug().Msgf("Mergetarget details: %v", mergetarget.StringNoACL()) // } - continue targetloop + failed = true + return false } } + return true + }) + if failed { + continue targetloop } for _, mfi := range mergeapprovers { res, err := mfi.mergefunc(o, mergetarget) @@ -422,20 +427,24 @@ func (os *Objects) Merge(attrtomerge []Attribute, o *Object) bool { if res != nil { // Custom merge - how do we handle this? ui.Fatal().Msgf("Custom merge function not supported yet") - return false } } // ui.Trace().Msgf("Merging %v with %v on attribute %v", o.Label(), mergetarget.Label(), mergeattr.String()) mergetarget.Absorb(o) os.ReindexObject(mergetarget, false) - return true + merged = true + return false } } + return true + }) + if merged { + break } } } - return false + return merged } func (os *Objects) add(o *Object) { diff --git a/modules/engine/objecttype.go b/modules/engine/objecttype.go index f9c6d69..20eaaf1 100644 --- a/modules/engine/objecttype.go +++ b/modules/engine/objecttype.go @@ -91,15 +91,30 @@ func NewObjectType(name, lookup string) ObjectType { } func ObjectTypeLookup(lookup string) (ObjectType, bool) { - lowername := strings.ToLower(lookup) - objecttypemutex.RLock() - objecttype, found := objecttypenames[lowername] + objecttype, found := objecttypenames[lookup] + if found { + objecttypemutex.RUnlock() + return objecttype, true + } + + lowername := strings.ToLower(lookup) + objecttype, found = objecttypenames[lowername] objecttypemutex.RUnlock() - if !found { - return ObjectTypeOther, false + if found { + // lowercase version found, add the cased version too + objecttypemutex.Lock() + objecttypenames[lookup] = objecttype + objecttypemutex.Unlock() + return objecttype, found } - return objecttype, found + + // not found, we don't know what this is, but lets speed this up for next time + objecttypemutex.Lock() + objecttypenames[lookup] = ObjectTypeOther + objecttypemutex.Unlock() + + return ObjectTypeOther, false } func (ot ObjectType) String() string { diff --git a/modules/engine/pwnanalyzers.go b/modules/engine/pwnanalyzers.go index e3a1589..15cf03a 100644 --- a/modules/engine/pwnanalyzers.go +++ b/modules/engine/pwnanalyzers.go @@ -122,5 +122,5 @@ func Process(ao *Objects, cb ProgressCallbackFunc, l LoaderID, priority ProcessP } wg.Wait() - return nil // FIXME + return nil } diff --git a/modules/engine/securitydescriptor.go b/modules/engine/securitydescriptor.go index da6b19f..d287c8b 100644 --- a/modules/engine/securitydescriptor.go +++ b/modules/engine/securitydescriptor.go @@ -187,7 +187,7 @@ func ParseACLentry(odata []byte) (ACE, []byte, error) { } } - ace.SID, data, err = windowssecurity.ParseSID(data) + ace.SID, data, err = windowssecurity.BytesToSID(data) if err != nil { return ace, data, err } @@ -227,15 +227,17 @@ func (a ACL) IsObjectClassAccessAllowed(index int, testObject *Object, mask Mask // // The allowed SID might be a member of one or more groups matching a DENY ACE // This will never work for cross domain groups - so, found := ao.Find(ObjectSid, AttributeValueSID(currentPotentialDenySid)) - if found { - for _, memberOfSid := range so.MemberOfSID(true) { - if memberOfSid == allowedSid { - sidmatch = true - break - } - } - } + + // FIXME + // so, found := ao.Find(ObjectSid, AttributeValueSID(currentPotentialDenySid)) + // if found { + // for _, memberOfSid := range so.MemberOfSID(true) { + // if memberOfSid == allowedSid { + // sidmatch = true + // break + // } + // } + // } } if sidmatch && a.Entries[i].matchObjectClassAndGUID(testObject, mask, guid, ao) { @@ -540,13 +542,13 @@ func ParseSecurityDescriptor(data []byte) (SecurityDescriptor, error) { } var err error if OffsetOwner > 0 { - result.Owner, _, err = windowssecurity.ParseSID(data[OffsetOwner:]) + result.Owner, _, err = windowssecurity.BytesToSID(data[OffsetOwner:]) if err != nil { return result, err } } if OffsetGroup > 0 { - result.Group, _, err = windowssecurity.ParseSID(data[OffsetGroup:]) + result.Group, _, err = windowssecurity.BytesToSID(data[OffsetGroup:]) if err != nil { return result, err } diff --git a/modules/integrations/activedirectory/analyze/analyze-ad.go b/modules/integrations/activedirectory/analyze/analyze-ad.go index 3c136c8..aa083ca 100644 --- a/modules/integrations/activedirectory/analyze/analyze-ad.go +++ b/modules/integrations/activedirectory/analyze/analyze-ad.go @@ -49,11 +49,11 @@ var ( ObjectGuidOU = uuid.UUID{0xbf, 0x96, 0x7a, 0xa5, 0x0d, 0xe6, 0x11, 0xd0, 0xa2, 0x85, 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2} ObjectGuidAttributeSchema, _ = uuid.FromString("{BF967A80-0DE6-11D0-A285-00AA003049E2}") - AdministratorsSID, _ = windowssecurity.SIDFromString("S-1-5-32-544") - BackupOperatorsSID, _ = windowssecurity.SIDFromString("S-1-5-32-551") - PrintOperatorsSID, _ = windowssecurity.SIDFromString("S-1-5-32-550") - ServerOperatorsSID, _ = windowssecurity.SIDFromString("S-1-5-32-549") - EnterpriseDomainControllers, _ = windowssecurity.SIDFromString("S-1-5-9") + AdministratorsSID, _ = windowssecurity.ParseStringSID("S-1-5-32-544") + BackupOperatorsSID, _ = windowssecurity.ParseStringSID("S-1-5-32-551") + PrintOperatorsSID, _ = windowssecurity.ParseStringSID("S-1-5-32-550") + ServerOperatorsSID, _ = windowssecurity.ParseStringSID("S-1-5-32-549") + EnterpriseDomainControllers, _ = windowssecurity.ParseStringSID("S-1-5-9") GPLinkCache = engine.NewAttribute("gpLinkCache") @@ -498,9 +498,8 @@ func init() { engine.EdgeAnalyzer{ Description: "Allows someone to read a password of a managed service account", ObjectAnalyzer: func(o *engine.Object, ao *engine.Objects) { - msasds := o.AttrString(activedirectory.MSDSGroupMSAMembership) - for _, msasd := range msasds { - sd, err := engine.ParseSecurityDescriptor([]byte(msasd)) + o.Attr(activedirectory.MSDSGroupMSAMembership).Iterate(func(msads engine.AttributeValue) bool { + sd, err := engine.ParseSecurityDescriptor([]byte(msads.String())) if err == nil { for _, acl := range sd.DACL.Entries { if acl.Type == engine.ACETYPE_ACCESS_ALLOWED { @@ -508,7 +507,8 @@ func init() { } } } - } + return true + }) }, }, engine.EdgeAnalyzer{ @@ -922,14 +922,11 @@ func init() { continue } + // Apply this edge adminsdholder.EdgeTo(o, activedirectory.EdgeOverwritesACL) - dm := o.Members(false) - idm := o.Members(true) - _ = dm - _ = idm - for _, member := range o.Members(true) { - adminsdholder.EdgeTo(member, activedirectory.EdgeOverwritesACL) - } + ApplyToGroupMembers(o, func(target *engine.Object) { + adminsdholder.EdgeTo(target, activedirectory.EdgeOverwritesACL) + }, true) } } }, @@ -939,7 +936,7 @@ func init() { Loader.AddProcessor(func(ao *engine.Objects) { // Add our known SIDs if they're missing for sid, name := range windowssecurity.KnownSIDs { - binsid, err := windowssecurity.SIDFromString(sid) + binsid, err := windowssecurity.ParseStringSID(sid) if err != nil { ui.Fatal().Msgf("Problem parsing SID %v", sid) } @@ -962,13 +959,13 @@ func init() { Loader.AddProcessor(func(ao *engine.Objects) { // Generate member of chains - everyonesid, _ := windowssecurity.SIDFromString("S-1-1-0") + everyonesid, _ := windowssecurity.ParseStringSID("S-1-1-0") everyone := FindWellKnown(ao, everyonesid) if everyone == nil { ui.Fatal().Msgf("Could not locate Everyone, aborting - this should at least have been added during earlier preprocessing") } - authenticateduserssid, _ := windowssecurity.SIDFromString("S-1-5-11") + authenticateduserssid, _ := windowssecurity.ParseStringSID("S-1-5-11") authenticatedusers := FindWellKnown(ao, authenticateduserssid) if authenticatedusers == nil { ui.Fatal().Msgf("Could not locate Authenticated Users, aborting - this should at least have been added during earlier preprocessing") @@ -992,11 +989,11 @@ func init() { var guids []engine.AttributeValue for _, class := range objectclasses { if oto, found := ao.Find(engine.LDAPDisplayName, class); found { - if _, ok := oto.OneAttrRaw(activedirectory.SchemaIDGUID).(uuid.UUID); !ok { + if guid, ok := oto.OneAttr(activedirectory.SchemaIDGUID).(engine.AttributeValueGUID); !ok { ui.Debug().Msgf("%v", oto) ui.Fatal().Msgf("Sorry, could not translate SchemaIDGUID for class %v - I need a Schema to work properly", class) } else { - guids = append(guids, oto.OneAttr(activedirectory.SchemaIDGUID)) + guids = append(guids, guid) } } } @@ -1029,14 +1026,14 @@ func init() { sidbytes := []byte(sid) binary.LittleEndian.PutUint32(sidbytes[len(sid)-4:], uint32(rid)) primarygroup := ao.FindOrAddAdjacentSID(windowssecurity.SID(sidbytes), object) - primarygroup.AddMember(object) + object.EdgeTo(primarygroup, activedirectory.EdgeMemberOfGroup) } } // Crude special handling for Everyone and Authenticated Users if object.Type() == engine.ObjectTypeUser || object.Type() == engine.ObjectTypeComputer || object.Type() == engine.ObjectTypeManagedServiceAccount || object.Type() == engine.ObjectTypeForeignSecurityPrincipal || object.Type() == engine.ObjectTypeGroupManagedServiceAccount { - everyone.AddMember(object) - authenticatedusers.AddMember(object) + object.EdgeTo(everyone, activedirectory.EdgeMemberOfGroup) + object.EdgeTo(authenticatedusers, activedirectory.EdgeMemberOfGroup) } if lastlogon, ok := object.AttrTimestamp(activedirectory.LastLogonTimestamp); ok { @@ -1179,9 +1176,11 @@ func init() { Loader.AddProcessor(func(ao *engine.Objects) { for _, object := range ao.Slice() { if object.HasAttrValue(engine.Name, engine.AttributeValueString("Protected Users")) && object.SID().RID() == 525 { // "Protected Users" - for _, member := range object.Members(true) { - member.SetValues(engine.MetaProtectedUser, engine.AttributeValueInt(1)) - } + ApplyToGroupMembers(object, func(member *engine.Object) { + if member.Type() == engine.ObjectTypeComputer || member.Type() == engine.ObjectTypeUser { + member.SetValues(engine.MetaProtectedUser, engine.AttributeValueInt(1)) + } + }, true) } } }, @@ -1398,13 +1397,13 @@ func init() { Loader.AddProcessor(func(ao *engine.Objects) { for _, object := range ao.Slice() { // Object that is member of something - for _, memberof := range object.Attr(activedirectory.MemberOf).Slice() { + object.Attr(activedirectory.MemberOf).Iterate(func(memberof engine.AttributeValue) bool { group, found := ao.Find(engine.DistinguishedName, memberof) if !found { var sid engine.AttributeValueSID if stringsid, _, found := strings.Cut(memberof.String(), ",CN=ForeignSecurityPrincipals,"); found { // We can figure out what the SID is - if c, err := windowssecurity.SIDFromString(stringsid); err == nil { + if c, err := windowssecurity.ParseStringSID(stringsid); err == nil { sid = engine.AttributeValueSID(c) } ui.Info().Msgf("Missing Foreign-Security-Principal: %v is a member of %v, which is not found - adding enhanced synthetic group", object.DN(), memberof) @@ -1423,18 +1422,19 @@ func init() { ) ao.Add(group) } - group.AddMember(object) - } + object.EdgeTo(group, activedirectory.EdgeMemberOfGroup) + return true + }) // Group that contains members - for _, member := range object.Attr(activedirectory.Member).Slice() { + object.Attr(activedirectory.Member).Iterate(func(member engine.AttributeValue) bool { memberobject, found := ao.Find(engine.DistinguishedName, member) if !found { var sid engine.AttributeValueSID var category string if stringsid, _, found := strings.Cut(member.String(), ",CN=ForeignSecurityPrincipals,"); found { // We can figure out what the SID is - if c, err := windowssecurity.SIDFromString(stringsid); err == nil { + if c, err := windowssecurity.ParseStringSID(stringsid); err == nil { sid = engine.AttributeValueSID(c) category = "Foreign-Security-Principal" } @@ -1451,16 +1451,13 @@ func init() { ) ao.Add(memberobject) } - object.AddMember(memberobject) - } - - for _, member := range object.Members(false) { - member.EdgeTo(object, activedirectory.EdgeMemberOfGroup) - } + memberobject.EdgeTo(object, activedirectory.EdgeMemberOfGroup) + return true + }) } }, "MemberOf and Member resolution", - engine.AfterMerge, + engine.BeforeMerge, ) Loader.AddProcessor(func(ao *engine.Objects) { @@ -1553,3 +1550,36 @@ func init() { engine.AfterMerge, ) } + +func ApplyToGroupMembers(startGroup *engine.Object, af func(member *engine.Object), recursive bool) { + var appliedTo map[*engine.Object]struct{} + if recursive { + appliedTo = make(map[*engine.Object]struct{}) + } + + startGroup.EdgeIterator(engine.In, func(nextTarget *engine.Object, edge engine.EdgeBitmap) bool { + if edge.IsSet(activedirectory.EdgeMemberOfGroup) { + af(nextTarget) + if recursive { + appliedTo[nextTarget] = struct{}{} + applyToGroupMemberRecursive(nextTarget, af, appliedTo) + } + } + return true + }) +} + +func applyToGroupMemberRecursive(group *engine.Object, af func(nextTarget *engine.Object), appliedTo map[*engine.Object]struct{}) { + if _, found := appliedTo[group]; found { + return + } + + group.EdgeIterator(engine.In, func(target *engine.Object, edge engine.EdgeBitmap) bool { + if edge.IsSet(activedirectory.EdgeMemberOfGroup) { + af(target) + appliedTo[target] = struct{}{} + applyToGroupMemberRecursive(target, af, appliedTo) + } + return true + }) +} diff --git a/modules/integrations/activedirectory/analyze/gpoimport.go b/modules/integrations/activedirectory/analyze/gpoimport.go index e97ebe3..e8c0b46 100644 --- a/modules/integrations/activedirectory/analyze/gpoimport.go +++ b/modules/integrations/activedirectory/analyze/gpoimport.go @@ -212,7 +212,7 @@ func ImportGPOInfo(ginfo activedirectory.GPOdump, ao *engine.Objects) error { member, _ = ao.FindOrAdd(engine.SAMAccountName, engine.AttributeValueString(sidpair.MemberName)) } else { // Use the SID - membersid, err := windowssecurity.SIDFromString(sidpair.MemberSID) + membersid, err := windowssecurity.ParseStringSID(sidpair.MemberSID) if err == nil { member = ao.FindOrAddSID(membersid) } diff --git a/modules/integrations/activedirectory/rawobject.go b/modules/integrations/activedirectory/rawobject.go index e78f264..03b836c 100644 --- a/modules/integrations/activedirectory/rawobject.go +++ b/modules/integrations/activedirectory/rawobject.go @@ -10,6 +10,7 @@ import ( "github.com/lkarlslund/adalanche/modules/engine" "github.com/lkarlslund/adalanche/modules/ui" "github.com/lkarlslund/adalanche/modules/util" + "github.com/lkarlslund/adalanche/modules/windowssecurity" ldap "github.com/lkarlslund/ldap/v3" ) @@ -152,7 +153,8 @@ func EncodeAttributeData(attribute engine.Attribute, values []string) engine.Att ui.Warn().Msgf("Failed to convert attribute %v value %2x to GUID: %v", attribute.String(), []byte(value), err) } case ObjectSid, SIDHistory, SecurityIdentifier, CreatorSID: - attributevalue = engine.AttributeValueSID(value) + sid, _, _ := windowssecurity.BytesToSID([]byte(value)) + attributevalue = engine.AttributeValueSID(sid) default: // AUTO CONVERSION - WHAT COULD POSSIBLY GO WRONG if value == "true" || value == "TRUE" { @@ -203,6 +205,7 @@ func EncodeAttributeData(attribute engine.Attribute, values []string) engine.Att result = new } - avsPool.Put(avs[:0]) + avs = avs[:0] + avsPool.Put(avs) return result } diff --git a/modules/integrations/localmachine/analyze/import.go b/modules/integrations/localmachine/analyze/import.go index 95a4980..4985272 100644 --- a/modules/integrations/localmachine/analyze/import.go +++ b/modules/integrations/localmachine/analyze/import.go @@ -20,13 +20,13 @@ func ImportCollectorInfo(ao *engine.Objects, cinfo localmachine.Info) (*engine.O var existing bool // See if the machine has a unique SID - localsid, err := windowssecurity.SIDFromString(cinfo.Machine.LocalSID) + localsid, err := windowssecurity.ParseStringSID(cinfo.Machine.LocalSID) if err != nil { return nil, fmt.Errorf("collected localmachine information for %v doesn't contain valid local machine SID (%v): %v", cinfo.Machine.Name, cinfo.Machine.LocalSID, err) } if cinfo.Machine.IsDomainJoined { - domainsid, err := windowssecurity.SIDFromString(cinfo.Machine.ComputerDomainSID) + domainsid, err := windowssecurity.ParseStringSID(cinfo.Machine.ComputerDomainSID) if cinfo.Machine.ComputerDomainSID != "" && err == nil { computerobject, existing = ao.FindOrAdd( analyze.DomainJoinedSID, engine.AttributeValueSID(domainsid), @@ -109,9 +109,9 @@ func ImportCollectorInfo(ao *engine.Objects, cinfo localmachine.Info) (*engine.O // Local accounts should not merge, unless we're a DC, then it's OK to merge with the domain source uniquesource := cinfo.Machine.Name - // if isdomaincontroller { - // uniquesource = cinfo.Machine.Domain - // } + if isdomaincontroller { + uniquesource = cinfo.Machine.Domain + } // Don't set UniqueSource on the computer object, it needs to merge with the AD object! computerobject.SetFlex(engine.UniqueSource, uniquesource) @@ -157,7 +157,7 @@ func ImportCollectorInfo(ao *engine.Objects, cinfo localmachine.Info) (*engine.O if user.NoChangePassword { uac += 0x10000 } - usid, err := windowssecurity.SIDFromString(user.SID) + usid, err := windowssecurity.ParseStringSID(user.SID) if err == nil { user := ao.AddNew( engine.IgnoreBlanks, @@ -184,7 +184,7 @@ func ImportCollectorInfo(ao *engine.Objects, cinfo localmachine.Info) (*engine.O ao.Add(groupscontainer) groupscontainer.ChildOf(computerobject) for _, group := range cinfo.Groups { - groupsid, err := windowssecurity.SIDFromString(group.SID) + groupsid, err := windowssecurity.ParseStringSID(group.SID) // Potential translation // groupsid = MapSID(originalsid, localsid, groupsid) @@ -203,14 +203,14 @@ func ImportCollectorInfo(ao *engine.Objects, cinfo localmachine.Info) (*engine.O for _, member := range group.Members { var membersid windowssecurity.SID if member.SID != "" { - membersid, err = windowssecurity.SIDFromString(member.SID) + membersid, err = windowssecurity.ParseStringSID(member.SID) if err != nil { ui.Warn().Msgf("Can't convert local group member SID %v: %v", member.SID, err) continue } } else { // Some members show up with the SID in the name field FML - membersid, err = windowssecurity.SIDFromString(member.Name) + membersid, err = windowssecurity.ParseStringSID(member.Name) if err != nil { ui.Info().Msgf("Fallback SID translation on %v failed: %v", member.Name, err) continue @@ -270,7 +270,7 @@ func ImportCollectorInfo(ao *engine.Objects, cinfo localmachine.Info) (*engine.O // USERS THAT HAVE SESSIONS ON THE MACHINE ONCE IN WHILE for _, login := range cinfo.LoginPopularity.Day { - usersid, err := windowssecurity.SIDFromString(login.SID) + usersid, err := windowssecurity.ParseStringSID(login.SID) if err != nil { ui.Warn().Msgf("Can't convert local user SID %v: %v", login.SID, err) continue @@ -300,7 +300,7 @@ func ImportCollectorInfo(ao *engine.Objects, cinfo localmachine.Info) (*engine.O } for _, login := range cinfo.LoginPopularity.Week { - usersid, err := windowssecurity.SIDFromString(login.SID) + usersid, err := windowssecurity.ParseStringSID(login.SID) if err != nil { ui.Warn().Msgf("Can't convert local user SID %v: %v", login.SID, err) continue @@ -329,7 +329,7 @@ func ImportCollectorInfo(ao *engine.Objects, cinfo localmachine.Info) (*engine.O } for _, login := range cinfo.LoginPopularity.Month { - usersid, err := windowssecurity.SIDFromString(login.SID) + usersid, err := windowssecurity.ParseStringSID(login.SID) if err != nil { ui.Warn().Msgf("Can't convert local user SID %v: %v", login.SID, err) continue @@ -360,7 +360,7 @@ func ImportCollectorInfo(ao *engine.Objects, cinfo localmachine.Info) (*engine.O // AUTOLOGIN CREDENTIALS - ONLY IF DOMAIN JOINED AND IT'S TO THIS DOMAIN if cinfo.Machine.DefaultUsername != "" && cinfo.Machine.DefaultDomain != "" && - cinfo.Machine.DefaultDomain == cinfo.Machine.Domain { + strings.EqualFold(cinfo.Machine.DefaultDomain, cinfo.Machine.Domain) { // NETBIOS name for domain check FIXME user, _ := ao.FindOrAdd( engine.NetbiosDomain, engine.AttributeValueString(cinfo.Machine.DefaultDomain), @@ -376,8 +376,9 @@ func ImportCollectorInfo(ao *engine.Objects, cinfo localmachine.Info) (*engine.O ao.Add(servicescontainer) servicescontainer.ChildOf(computerobject) + // All services are a member of this group localservicesgroup := ao.AddNew( - activedirectory.ObjectSid, engine.AttributeValueSID(windowssecurity.LocalServiceSID), + activedirectory.ObjectSid, engine.AttributeValueSID(windowssecurity.ServicesSID), engine.DownLevelLogonName, cinfo.Machine.Name+"\\Services", engine.UniqueSource, cinfo.Machine.Name, ) @@ -394,39 +395,81 @@ func ImportCollectorInfo(ao *engine.Objects, cinfo localmachine.Info) (*engine.O ) ao.Add(serviceobject) serviceobject.ChildOf(servicescontainer) + serviceobject.EdgeTo(localservicesgroup, engine.EdgeMemberOfGroup) + computerobject.EdgeTo(serviceobject, EdgeHosts) - if serviceaccountSID, err := windowssecurity.SIDFromString(service.AccountSID); err == nil && serviceaccountSID.Component(2) == 21 { + var serviceaccountSID windowssecurity.SID - // Potential translation - // serviceaccountSID = MapSID(originalsid, localsid, serviceaccountSID) + // If we have the SID use that + if service.AccountSID != "" { + serviceaccountSID, err = windowssecurity.ParseStringSID(service.AccountSID) + if err != nil { + ui.Warn().Msgf("Service account SID (%v) parsing problem: %v", service.AccountSID, err) + } + } - nameparts := strings.Split(service.Account, "\\") - if len(nameparts) == 2 && nameparts[0] != cinfo.Machine.Domain { // FIXME - NETBIOS NAMES ARE KILLIG US - svcaccount, _ := ao.FindOrAdd( - activedirectory.ObjectSid, engine.AttributeValueSID(serviceaccountSID), - engine.IgnoreBlanks, - engine.DownLevelLogonName, service.Account, - // activedirectory.SAMAccountName, engine.AttributeValueString(nameparts[1]), - // activedirectory.ObjectCategorySimple, engine.AttributeValueString("Person"), + // Some service don't have SID, just the name + if serviceaccountSID.IsBlank() { + if strings.EqualFold(service.Account, "LocalSystem") { + serviceaccountSID = windowssecurity.SystemSID + } + } + + var svcaccount *engine.Object + if !serviceaccountSID.IsBlank() { + svcaccount = ao.AddNew( + activedirectory.ObjectSid, engine.AttributeValueSID(serviceaccountSID), + ) + if serviceaccountSID.StripRID() == localsid || serviceaccountSID.Component(2) != 21 { + svcaccount.SetFlex( + engine.UniqueSource, uniquesource, ) - if serviceaccountSID.StripRID() == localsid || serviceaccountSID.Component(2) != 21 { + + nameparts := strings.Split(service.Account, "\\") + if len(nameparts) == 2 && strings.EqualFold(nameparts[0], cinfo.Machine.Domain) { svcaccount.SetFlex( - engine.UniqueSource, uniquesource, + engine.DownLevelLogonName, service.Account, ) } + } + } + if svcaccount == nil { + if service.Account != "" { + nameparts := strings.Split(service.Account, "\\") + // account can be USER, .\USER, DOMAIN\USER (come on!) + if len(nameparts) == 2 && (nameparts[0] == "." || strings.EqualFold(nameparts[0], cinfo.Machine.Domain)) { + svcaccount, _ = ao.FindOrAdd( + engine.DownLevelLogonName, engine.AttributeValueString(cinfo.Machine.Domain+"\\"+nameparts[1]), + ) + } else if len(nameparts) == 1 { + // no \\ in name, just a user name!? + svcaccount, _ = ao.FindOrAdd( + engine.DownLevelLogonName, engine.AttributeValueString(cinfo.Machine.Domain+"\\"+nameparts[0]), + ) + } + } + } + + // Did we somehow manage to find an account? + if svcaccount != nil { + if serviceaccountSID.Component(2) == 21 || serviceaccountSID.Component(2) == 32 { + // Foreign to computer, so it gets a direct edge computerobject.EdgeTo(svcaccount, EdgeHasServiceAccountCredentials) + } + + if serviceaccountSID != windowssecurity.LocalServiceSID { serviceobject.EdgeTo(svcaccount, analyze.EdgeAuthenticatesAs) } - } else if strings.EqualFold(service.Account, "LocalSystem") { - serviceobject.EdgeTo(computerobject, analyze.EdgeAuthenticatesAs) + } else if service.Account != "" || service.AccountSID != "" { + ui.Warn().Msgf("Unhandled service credentials %+v", service) } // Change service executable via registry if service.RegistryOwner != "" { - ro, err := windowssecurity.SIDFromString(service.RegistryOwner) + ro, err := windowssecurity.ParseStringSID(service.RegistryOwner) if err == nil { o := ao.AddNew( activedirectory.ObjectSid, engine.AttributeValueSID(ro), @@ -488,7 +531,7 @@ func ImportCollectorInfo(ao *engine.Objects, cinfo localmachine.Info) (*engine.O serviceimageobject.EdgeTo(serviceobject, EdgeExecuted) serviceimageobject.ChildOf(serviceobject) - if ownersid, err := windowssecurity.SIDFromString(service.ImageExecutableOwner); err == nil { + if ownersid, err := windowssecurity.ParseStringSID(service.ImageExecutableOwner); err == nil { // Potential translation if ownersid.Component(2) == 80 /* Service user */ { continue @@ -573,7 +616,7 @@ func ImportCollectorInfo(ao *engine.Objects, cinfo localmachine.Info) (*engine.O } for _, sidstring := range pi.AssignedSIDs { - sid, err := windowssecurity.SIDFromString(sidstring) + sid, err := windowssecurity.ParseStringSID(sidstring) if err != nil { ui.Error().Msgf("Invalid SID %v: %v", sidstring, err) continue diff --git a/modules/integrations/localmachine/collect/main.go b/modules/integrations/localmachine/collect/main.go index 0d11b98..f448cdb 100644 --- a/modules/integrations/localmachine/collect/main.go +++ b/modules/integrations/localmachine/collect/main.go @@ -569,83 +569,79 @@ func Collect() (localmachine.Info, error) { service_key, err := registry.OpenKey(services_key, service, registry.READ|registry.ENUMERATE_SUB_KEYS|registry.WOW64_64KEY) if err == nil { - defer service_key.Close() - displayname, _, _ := service_key.GetStringValue("DisplayName") - description, _, _ := service_key.GetStringValue("Description") - objectname, _, _ := service_key.GetStringValue("ObjectName") - objectnamesid, _ := winio.LookupSidByName(objectname) - imagepath, _, _ := service_key.GetStringValue("ImagePath") - - // Grab ImagePath key security - registryowner, registrydacl, _ := windowssecurity.GetOwnerAndDACL(`MACHINE\SYSTEM\CurrentControlSet\Services\`+service+``, windows.SE_REGISTRY_KEY) - - // if strings.HasSuffix(imagepath, "locator.exe") { - // ui.Info().Msg("Jackpot!") - // } - - // let's see if we can grab a DACL - var imagepathowner string - var imageexecutable string - var imagepathdacl []byte - - if imagepath != "" { - // Windows service executable names is a hot effin mess - if strings.HasPrefix(strings.ToLower(imagepath), `system32\`) { - // Avoid mapping on 32-bit on 64-bit SYSWOW - imagepath = `%SystemRoot%\` + imagepath - } else if strings.HasPrefix(imagepath, `\SystemRoot\`) { - imagepath = `%SystemRoot%\` + imagepath[12:] - } else if strings.HasPrefix(imagepath, `\??\`) { - imagepath = imagepath[4:] - } - - // find the executable name ... windows .... arrrgh - var executable string - if imagepath[0] == '"' { - // Quoted - nextquote := strings.Index(imagepath[1:], `"`) - if nextquote != -1 { - executable = imagepath[1 : nextquote+1] + stype, _, _ := service_key.GetIntegerValue("Type") + if stype >= 16 { + displayname, _, _ := service_key.GetStringValue("DisplayName") + description, _, _ := service_key.GetStringValue("Description") + objectname, _, _ := service_key.GetStringValue("ObjectName") + objectnamesid, _ := winio.LookupSidByName(objectname) + imagepath, _, _ := service_key.GetStringValue("ImagePath") + requiredPrivileges, _, _ := service_key.GetStringsValue("RequiredPrivileges") + start, _, _ := service_key.GetIntegerValue("Start") + + // Grab ImagePath key security + registryowner, registrydacl, _ := windowssecurity.GetOwnerAndDACL(`MACHINE\SYSTEM\CurrentControlSet\Services\`+service+``, windows.SE_REGISTRY_KEY) + + // let's see if we can grab a DACL + var imagepathowner string + var imageexecutable string + var imagepathdacl []byte + + if imagepath != "" { + // Windows service executable names is a hot effin mess + if strings.HasPrefix(strings.ToLower(imagepath), `system32\`) { + // Avoid mapping on 32-bit on 64-bit SYSWOW + imagepath = `%SystemRoot%\` + imagepath + } else if strings.HasPrefix(imagepath, `\SystemRoot\`) { + imagepath = `%SystemRoot%\` + imagepath[12:] + } else if strings.HasPrefix(imagepath, `\??\`) { + imagepath = imagepath[4:] } - } else { - // Unquoted - trypath := imagepath - for { - statpath := resolvepath(trypath) - ui.Debug().Msgf("Trying %v -> %v", trypath, statpath) - if _, err = os.Stat(statpath); err == nil { - executable = trypath - break - } - lastspace := strings.LastIndex(trypath, " ") - if lastspace == -1 { - break // give up + + // find the executable name ... windows .... arrrgh + var executable string + if imagepath[0] == '"' { + // Quoted + nextquote := strings.Index(imagepath[1:], `"`) + if nextquote != -1 { + executable = imagepath[1 : nextquote+1] } - trypath = imagepath[:lastspace] - if !strings.HasSuffix(strings.ToLower(trypath), ".exe") { - trypath += ".exe" + } else { + // Unquoted + trypath := imagepath + for { + statpath := resolvepath(trypath) + ui.Debug().Msgf("Trying %v -> %v", trypath, statpath) + if _, err = os.Stat(statpath); err == nil { + executable = trypath + break + } + lastspace := strings.LastIndex(trypath, " ") + if lastspace == -1 { + break // give up + } + trypath = imagepath[:lastspace] + if !strings.HasSuffix(strings.ToLower(trypath), ".exe") { + trypath += ".exe" + } } } - } - ui.Debug().Msgf("Imagepath %v is mapped to executable %v", imagepath, executable) - executable = resolvepath(executable) - imageexecutable = executable - if executable != "" { - ownersid, dacl, err := windowssecurity.GetOwnerAndDACL(executable, windows.SE_FILE_OBJECT) - if err == nil { - imagepathowner = ownersid.String() - imagepathdacl = dacl + ui.Debug().Msgf("Imagepath %v is mapped to executable %v", imagepath, executable) + executable = resolvepath(executable) + imageexecutable = executable + if executable != "" { + ownersid, dacl, err := windowssecurity.GetOwnerAndDACL(executable, windows.SE_FILE_OBJECT) + if err == nil { + imagepathowner = ownersid.String() + imagepathdacl = dacl + } else { + ui.Warn().Msgf("Problem getting security info for %v: %v", executable, err) + } } else { - ui.Warn().Msgf("Problem getting security info for %v: %v", executable, err) + ui.Warn().Msgf("Could not resolve executable %v", imagepath) } - } else { - ui.Warn().Msgf("Could not resolve executable %v", imagepath) } - } - start, _, _ := service_key.GetIntegerValue("Start") - stype, _, _ := service_key.GetIntegerValue("Type") - if stype >= 16 { servicesinfo = append(servicesinfo, localmachine.Service{ RegistryOwner: registryowner.String(), RegistryDACL: registrydacl, @@ -660,20 +656,22 @@ func Collect() (localmachine.Info, error) { Type: int(stype), Account: objectname, AccountSID: objectnamesid, + RequiredPrivileges: requiredPrivileges, }) } + service_key.Close() } } } } // LOCAL USERS AND GROUPS - domainsid, _ := windowssecurity.SIDFromString(machineinfo.ComputerDomainSID) + domainsid, _ := windowssecurity.ParseStringSID(machineinfo.ComputerDomainSID) var usersinfo localmachine.Users users, _ := winapi.ListLocalUsers() for _, user := range users { - usersid, _ := windowssecurity.SIDFromString(user.SID) + usersid, _ := windowssecurity.ParseStringSID(user.SID) if machineinfo.IsDomainJoined && usersid.StripRID() == domainsid.StripRID() { // This is a domain account, so we're running on a DC? skip it continue diff --git a/modules/integrations/localmachine/collect/winapi.go b/modules/integrations/localmachine/collect/winapi.go index 250eaa1..eca4c10 100644 --- a/modules/integrations/localmachine/collect/winapi.go +++ b/modules/integrations/localmachine/collect/winapi.go @@ -202,14 +202,14 @@ var ( lsaStorePrivateData = advapi.NewProc("LsaStorePrivateData") ) -// typedef struct _LSA_OBJECT_ATTRIBUTES { -// ULONG Length; -// HANDLE RootDirectory; -// PLSA_UNICODE_STRING ObjectName; -// ULONG Attributes; -// PVOID SecurityDescriptor; -// PVOID SecurityQualityOfService; -// } LSA_OBJECT_ATTRIBUTES, *PLSA_OBJECT_ATTRIBUTES; +// typedef struct _LSA_OBJECT_ATTRIBUTES { +// ULONG Length; +// HANDLE RootDirectory; +// PLSA_UNICODE_STRING ObjectName; +// ULONG Attributes; +// PVOID SecurityDescriptor; +// PVOID SecurityQualityOfService; +// } LSA_OBJECT_ATTRIBUTES, *PLSA_OBJECT_ATTRIBUTES; type _LSA_OBJECT_ATTRIBUTES struct { Length uint32 RootDirectory syscall.Handle @@ -219,10 +219,11 @@ type _LSA_OBJECT_ATTRIBUTES struct { SecurityQualityOfService uintptr } -// typedef struct _LSA_UNICODE_STRING { -// USHORT Length; -// USHORT MaximumLength; -// } LSA_UNICODE_STRING, *PLSA_UNICODE_STRING; +// typedef struct _LSA_UNICODE_STRING { +// USHORT Length; +// USHORT MaximumLength; +// } LSA_UNICODE_STRING, *PLSA_UNICODE_STRING; +// // https://docs.microsoft.com/en-us/windows/desktop/api/lsalookup/ns-lsalookup-_lsa_unicode_string type _LSA_UNICODE_STRING struct { Length uint16 @@ -316,11 +317,13 @@ const ( ) // NTSTATUS LsaOpenPolicy( -// PLSA_UNICODE_STRING SystemName, -// PLSA_OBJECT_ATTRIBUTES ObjectAttributes, -// ACCESS_MASK DesiredAccess, -// PLSA_HANDLE PolicyHandle -// ); +// +// PLSA_UNICODE_STRING SystemName, +// PLSA_OBJECT_ATTRIBUTES ObjectAttributes, +// ACCESS_MASK DesiredAccess, +// PLSA_HANDLE PolicyHandle +// ); +// // https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-lsaopenpolicy func LsaOpenPolicy(system string, access uint32) (*syscall.Handle, error) { // Docs say this is not used, but the structure needs to be @@ -346,7 +349,9 @@ func LsaOpenPolicy(system string, access uint32) (*syscall.Handle, error) { } // NTSTATUS LsaClose( -// LSA_HANDLE ObjectHandle +// +// LSA_HANDLE ObjectHandle +// // ); // https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-lsaclose func LsaClose(hPolicy syscall.Handle) error { @@ -360,12 +365,14 @@ func LsaClose(hPolicy syscall.Handle) error { } // NTSTATUS LsaEnumerateAccountRights( -// LSA_HANDLE PolicyHandle, -// PSID AccountSid, -// PLSA_UNICODE_STRING *UserRights, -// PULONG CountOfRights +// +// LSA_HANDLE PolicyHandle, +// PSID AccountSid, +// PLSA_UNICODE_STRING *UserRights, +// PULONG CountOfRights +// // ); -//https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-lsaenumerateaccountrights +// https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-lsaenumerateaccountrights func LsaEnumerateAccountRights(hPolicy syscall.Handle, sid *syscall.SID) ([]string, error) { var rights uintptr var count uint32 @@ -392,10 +399,12 @@ func LsaEnumerateAccountRights(hPolicy syscall.Handle, sid *syscall.SID) ([]stri } // NTSTATUS LsaEnumerateAccountsWithUserRight( -// [in] LSA_HANDLE PolicyHandle, -// [in] PLSA_UNICODE_STRING UserRight, -// [out] PVOID *Buffer, -// [out] PULONG CountReturned +// +// [in] LSA_HANDLE PolicyHandle, +// [in] PLSA_UNICODE_STRING UserRight, +// [out] PVOID *Buffer, +// [out] PULONG CountReturned +// // ); // https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaenumerateaccountswithuserright func LsaEnumerateAccountsWithUserRight(hPolicy syscall.Handle, userright string) ([]windowssecurity.SID, error) { @@ -421,7 +430,7 @@ func LsaEnumerateAccountsWithUserRight(hPolicy syscall.Handle, userright string) for i := 0; i < int(count); i++ { nativesid := ((*[32768]uintptr)(unsafe.Pointer(bufferptr)))[i] - sid, err := windowssecurity.SIDFromBytes(nativesid) + sid, err := windowssecurity.SIDFromPtr(nativesid) if err != nil { return nil, err } @@ -431,10 +440,12 @@ func LsaEnumerateAccountsWithUserRight(hPolicy syscall.Handle, userright string) } // NTSTATUS LsaAddAccountRights( -// LSA_HANDLE PolicyHandle, -// PSID AccountSid, -// PLSA_UNICODE_STRING UserRights, -// ULONG CountOfRights +// +// LSA_HANDLE PolicyHandle, +// PSID AccountSid, +// PLSA_UNICODE_STRING UserRights, +// ULONG CountOfRights +// // ); // https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-lsaaddaccountrights func LsaAddAccountRights(hPolicy syscall.Handle, sid *syscall.SID, rights []string) error { @@ -455,13 +466,15 @@ func LsaAddAccountRights(hPolicy syscall.Handle, sid *syscall.SID, rights []stri } // NTSTATUS LsaRemoveAccountRights( -// LSA_HANDLE PolicyHandle, -// PSID AccountSid, -// BOOLEAN AllRights, -// PLSA_UNICODE_STRING UserRights, -// ULONG CountOfRights +// +// LSA_HANDLE PolicyHandle, +// PSID AccountSid, +// BOOLEAN AllRights, +// PLSA_UNICODE_STRING UserRights, +// ULONG CountOfRights +// // ); -//https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-lsaremoveaccountrights +// https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-lsaremoveaccountrights func LsaRemoveAccountRights(hPolicy syscall.Handle, sid *syscall.SID, removeAll bool, rights []string) error { var lsaRights []_LSA_UNICODE_STRING if !removeAll { @@ -483,7 +496,9 @@ func LsaRemoveAccountRights(hPolicy syscall.Handle, sid *syscall.SID, removeAll } // ULONG LsaNtStatusToWinError( -// NTSTATUS Status +// +// NTSTATUS Status +// // ); // https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-lsantstatustowinerror func LsaNtStatusToWinError(status uintptr) error { @@ -495,7 +510,9 @@ func LsaNtStatusToWinError(status uintptr) error { } // NTSTATUS LsaFreeMemory( -// PVOID Buffer +// +// PVOID Buffer +// // ); // https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-lsafreememory func LsaFreeMemory(buf uintptr) error { diff --git a/modules/integrations/localmachine/structs.go b/modules/integrations/localmachine/structs.go index 62e82c3..60b32f4 100644 --- a/modules/integrations/localmachine/structs.go +++ b/modules/integrations/localmachine/structs.go @@ -115,8 +115,9 @@ type Service struct { Start int `json:",omitempty"` Type int `json:",omitempty"` - Account string `json:",omitempty"` - AccountSID string `json:",omitempty"` + Account string `json:",omitempty"` + AccountSID string `json:",omitempty"` + RequiredPrivileges []string `json:",omitempty"` } type Software struct { diff --git a/modules/integrations/localmachine/structs_easyjson.go b/modules/integrations/localmachine/structs_easyjson.go index e981f32..e73852d 100644 --- a/modules/integrations/localmachine/structs_easyjson.go +++ b/modules/integrations/localmachine/structs_easyjson.go @@ -1180,6 +1180,29 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.Account = string(in.String()) case "AccountSID": out.AccountSID = string(in.String()) + case "RequiredPrivileges": + if in.IsNull() { + in.Skip() + out.RequiredPrivileges = nil + } else { + in.Delim('[') + if out.RequiredPrivileges == nil { + if !in.IsDelim(']') { + out.RequiredPrivileges = make([]string, 0, 4) + } else { + out.RequiredPrivileges = []string{} + } + } else { + out.RequiredPrivileges = (out.RequiredPrivileges)[:0] + } + for !in.IsDelim(']') { + var v18 string + v18 = string(in.String()) + out.RequiredPrivileges = append(out.RequiredPrivileges, v18) + in.WantComma() + } + in.Delim(']') + } default: in.SkipRecursive() } @@ -1320,6 +1343,25 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm } out.String(string(in.AccountSID)) } + if len(in.RequiredPrivileges) != 0 { + const prefix string = ",\"RequiredPrivileges\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + { + out.RawByte('[') + for v23, v24 := range in.RequiredPrivileges { + if v23 > 0 { + out.RawByte(',') + } + out.String(string(v24)) + } + out.RawByte(']') + } + } out.RawByte('}') } @@ -1703,9 +1745,9 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.AssignedSIDs = (out.AssignedSIDs)[:0] } for !in.IsDelim(']') { - var v22 string - v22 = string(in.String()) - out.AssignedSIDs = append(out.AssignedSIDs, v22) + var v25 string + v25 = string(in.String()) + out.AssignedSIDs = append(out.AssignedSIDs, v25) in.WantComma() } in.Delim(']') @@ -1740,11 +1782,11 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm } { out.RawByte('[') - for v23, v24 := range in.AssignedSIDs { - if v23 > 0 { + for v26, v27 := range in.AssignedSIDs { + if v26 > 0 { out.RawByte(',') } - out.String(string(v24)) + out.String(string(v27)) } out.RawByte(']') } @@ -1943,9 +1985,9 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.Addresses = (out.Addresses)[:0] } for !in.IsDelim(']') { - var v25 string - v25 = string(in.String()) - out.Addresses = append(out.Addresses, v25) + var v28 string + v28 = string(in.String()) + out.Addresses = append(out.Addresses, v28) in.WantComma() } in.Delim(']') @@ -2000,11 +2042,11 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm } { out.RawByte('[') - for v26, v27 := range in.Addresses { - if v26 > 0 { + for v29, v30 := range in.Addresses { + if v29 > 0 { out.RawByte(',') } - out.String(string(v27)) + out.String(string(v30)) } out.RawByte(']') } @@ -2072,9 +2114,9 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.NetworkInterfaces = (out.NetworkInterfaces)[:0] } for !in.IsDelim(']') { - var v28 NetworkInterfaceInfo - (v28).UnmarshalEasyJSON(in) - out.NetworkInterfaces = append(out.NetworkInterfaces, v28) + var v31 NetworkInterfaceInfo + (v31).UnmarshalEasyJSON(in) + out.NetworkInterfaces = append(out.NetworkInterfaces, v31) in.WantComma() } in.Delim(']') @@ -2109,11 +2151,11 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm } { out.RawByte('[') - for v29, v30 := range in.NetworkInterfaces { - if v29 > 0 { + for v32, v33 := range in.NetworkInterfaces { + if v32 > 0 { out.RawByte(',') } - (v30).MarshalEasyJSON(out) + (v33).MarshalEasyJSON(out) } out.RawByte(']') } @@ -2298,14 +2340,14 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.AppCache = (out.AppCache)[:0] } for !in.IsDelim(']') { - var v31 []uint8 + var v34 []uint8 if in.IsNull() { in.Skip() - v31 = nil + v34 = nil } else { - v31 = in.Bytes() + v34 = in.Bytes() } - out.AppCache = append(out.AppCache, v31) + out.AppCache = append(out.AppCache, v34) in.WantComma() } in.Delim(']') @@ -2544,11 +2586,11 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm } { out.RawByte('[') - for v33, v34 := range in.AppCache { - if v33 > 0 { + for v36, v37 := range in.AppCache { + if v36 > 0 { out.RawByte(',') } - out.Base64Bytes(v34) + out.Base64Bytes(v37) } out.RawByte(']') } @@ -2684,9 +2726,9 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.Day = (out.Day)[:0] } for !in.IsDelim(']') { - var v37 LoginCount - (v37).UnmarshalEasyJSON(in) - out.Day = append(out.Day, v37) + var v40 LoginCount + (v40).UnmarshalEasyJSON(in) + out.Day = append(out.Day, v40) in.WantComma() } in.Delim(']') @@ -2707,9 +2749,9 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.Week = (out.Week)[:0] } for !in.IsDelim(']') { - var v38 LoginCount - (v38).UnmarshalEasyJSON(in) - out.Week = append(out.Week, v38) + var v41 LoginCount + (v41).UnmarshalEasyJSON(in) + out.Week = append(out.Week, v41) in.WantComma() } in.Delim(']') @@ -2730,9 +2772,9 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.Month = (out.Month)[:0] } for !in.IsDelim(']') { - var v39 LoginCount - (v39).UnmarshalEasyJSON(in) - out.Month = append(out.Month, v39) + var v42 LoginCount + (v42).UnmarshalEasyJSON(in) + out.Month = append(out.Month, v42) in.WantComma() } in.Delim(']') @@ -2758,11 +2800,11 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.RawString("null") } else { out.RawByte('[') - for v40, v41 := range in.Day { - if v40 > 0 { + for v43, v44 := range in.Day { + if v43 > 0 { out.RawByte(',') } - (v41).MarshalEasyJSON(out) + (v44).MarshalEasyJSON(out) } out.RawByte(']') } @@ -2774,11 +2816,11 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.RawString("null") } else { out.RawByte('[') - for v42, v43 := range in.Week { - if v42 > 0 { + for v45, v46 := range in.Week { + if v45 > 0 { out.RawByte(',') } - (v43).MarshalEasyJSON(out) + (v46).MarshalEasyJSON(out) } out.RawByte(']') } @@ -2790,11 +2832,11 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.RawString("null") } else { out.RawByte('[') - for v44, v45 := range in.Month { - if v44 > 0 { + for v47, v48 := range in.Month { + if v47 > 0 { out.RawByte(',') } - (v45).MarshalEasyJSON(out) + (v48).MarshalEasyJSON(out) } out.RawByte(']') } @@ -2959,9 +3001,9 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.Users = (out.Users)[:0] } for !in.IsDelim(']') { - var v46 User - (v46).UnmarshalEasyJSON(in) - out.Users = append(out.Users, v46) + var v49 User + (v49).UnmarshalEasyJSON(in) + out.Users = append(out.Users, v49) in.WantComma() } in.Delim(']') @@ -2982,9 +3024,9 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.Groups = (out.Groups)[:0] } for !in.IsDelim(']') { - var v47 Group - (v47).UnmarshalEasyJSON(in) - out.Groups = append(out.Groups, v47) + var v50 Group + (v50).UnmarshalEasyJSON(in) + out.Groups = append(out.Groups, v50) in.WantComma() } in.Delim(']') @@ -3005,9 +3047,9 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.Shares = (out.Shares)[:0] } for !in.IsDelim(']') { - var v48 Share - (v48).UnmarshalEasyJSON(in) - out.Shares = append(out.Shares, v48) + var v51 Share + (v51).UnmarshalEasyJSON(in) + out.Shares = append(out.Shares, v51) in.WantComma() } in.Delim(']') @@ -3028,9 +3070,9 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.Services = (out.Services)[:0] } for !in.IsDelim(']') { - var v49 Service - (v49).UnmarshalEasyJSON(in) - out.Services = append(out.Services, v49) + var v52 Service + (v52).UnmarshalEasyJSON(in) + out.Services = append(out.Services, v52) in.WantComma() } in.Delim(']') @@ -3051,9 +3093,9 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.Software = (out.Software)[:0] } for !in.IsDelim(']') { - var v50 Software - (v50).UnmarshalEasyJSON(in) - out.Software = append(out.Software, v50) + var v53 Software + (v53).UnmarshalEasyJSON(in) + out.Software = append(out.Software, v53) in.WantComma() } in.Delim(']') @@ -3074,9 +3116,9 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.Tasks = (out.Tasks)[:0] } for !in.IsDelim(']') { - var v51 RegisteredTask - (v51).UnmarshalEasyJSON(in) - out.Tasks = append(out.Tasks, v51) + var v54 RegisteredTask + (v54).UnmarshalEasyJSON(in) + out.Tasks = append(out.Tasks, v54) in.WantComma() } in.Delim(']') @@ -3097,9 +3139,9 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.Privileges = (out.Privileges)[:0] } for !in.IsDelim(']') { - var v52 Privilege - (v52).UnmarshalEasyJSON(in) - out.Privileges = append(out.Privileges, v52) + var v55 Privilege + (v55).UnmarshalEasyJSON(in) + out.Privileges = append(out.Privileges, v55) in.WantComma() } in.Delim(']') @@ -3174,11 +3216,11 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm } { out.RawByte('[') - for v53, v54 := range in.Users { - if v53 > 0 { + for v56, v57 := range in.Users { + if v56 > 0 { out.RawByte(',') } - (v54).MarshalEasyJSON(out) + (v57).MarshalEasyJSON(out) } out.RawByte(']') } @@ -3193,11 +3235,11 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm } { out.RawByte('[') - for v55, v56 := range in.Groups { - if v55 > 0 { + for v58, v59 := range in.Groups { + if v58 > 0 { out.RawByte(',') } - (v56).MarshalEasyJSON(out) + (v59).MarshalEasyJSON(out) } out.RawByte(']') } @@ -3212,11 +3254,11 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm } { out.RawByte('[') - for v57, v58 := range in.Shares { - if v57 > 0 { + for v60, v61 := range in.Shares { + if v60 > 0 { out.RawByte(',') } - (v58).MarshalEasyJSON(out) + (v61).MarshalEasyJSON(out) } out.RawByte(']') } @@ -3231,11 +3273,11 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm } { out.RawByte('[') - for v59, v60 := range in.Services { - if v59 > 0 { + for v62, v63 := range in.Services { + if v62 > 0 { out.RawByte(',') } - (v60).MarshalEasyJSON(out) + (v63).MarshalEasyJSON(out) } out.RawByte(']') } @@ -3250,11 +3292,11 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm } { out.RawByte('[') - for v61, v62 := range in.Software { - if v61 > 0 { + for v64, v65 := range in.Software { + if v64 > 0 { out.RawByte(',') } - (v62).MarshalEasyJSON(out) + (v65).MarshalEasyJSON(out) } out.RawByte(']') } @@ -3269,11 +3311,11 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm } { out.RawByte('[') - for v63, v64 := range in.Tasks { - if v63 > 0 { + for v66, v67 := range in.Tasks { + if v66 > 0 { out.RawByte(',') } - (v64).MarshalEasyJSON(out) + (v67).MarshalEasyJSON(out) } out.RawByte(']') } @@ -3288,11 +3330,11 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm } { out.RawByte('[') - for v65, v66 := range in.Privileges { - if v65 > 0 { + for v68, v69 := range in.Privileges { + if v68 > 0 { out.RawByte(',') } - (v66).MarshalEasyJSON(out) + (v69).MarshalEasyJSON(out) } out.RawByte(']') } @@ -3389,9 +3431,9 @@ func easyjson6a975c40DecodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm out.Members = (out.Members)[:0] } for !in.IsDelim(']') { - var v67 Member - (v67).UnmarshalEasyJSON(in) - out.Members = append(out.Members, v67) + var v70 Member + (v70).UnmarshalEasyJSON(in) + out.Members = append(out.Members, v70) in.WantComma() } in.Delim(']') @@ -3446,11 +3488,11 @@ func easyjson6a975c40EncodeGithubComLkarlslundAdalancheModulesIntegrationsLocalm } { out.RawByte('[') - for v68, v69 := range in.Members { - if v68 > 0 { + for v71, v72 := range in.Members { + if v71 > 0 { out.RawByte(',') } - (v69).MarshalEasyJSON(out) + (v72).MarshalEasyJSON(out) } out.RawByte(']') } diff --git a/modules/integrations/localmachine/structs_gen.go b/modules/integrations/localmachine/structs_gen.go index 091ae31..8dfe277 100644 --- a/modules/integrations/localmachine/structs_gen.go +++ b/modules/integrations/localmachine/structs_gen.go @@ -4786,6 +4786,25 @@ func (z *Service) DecodeMsg(dc *msgp.Reader) (err error) { err = msgp.WrapError(err, "AccountSID") return } + case "RequiredPrivileges": + var zb0002 uint32 + zb0002, err = dc.ReadArrayHeader() + if err != nil { + err = msgp.WrapError(err, "RequiredPrivileges") + return + } + if cap(z.RequiredPrivileges) >= int(zb0002) { + z.RequiredPrivileges = (z.RequiredPrivileges)[:zb0002] + } else { + z.RequiredPrivileges = make([]string, zb0002) + } + for za0001 := range z.RequiredPrivileges { + z.RequiredPrivileges[za0001], err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "RequiredPrivileges", za0001) + return + } + } default: err = dc.Skip() if err != nil { @@ -4799,9 +4818,9 @@ func (z *Service) DecodeMsg(dc *msgp.Reader) (err error) { // EncodeMsg implements msgp.Encodable func (z *Service) EncodeMsg(en *msgp.Writer) (err error) { - // map header, size 13 + // map header, size 14 // write "RegistryOwner" - err = en.Append(0x8d, 0xad, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4f, 0x77, 0x6e, 0x65, 0x72) + err = en.Append(0x8e, 0xad, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4f, 0x77, 0x6e, 0x65, 0x72) if err != nil { return } @@ -4930,15 +4949,32 @@ func (z *Service) EncodeMsg(en *msgp.Writer) (err error) { err = msgp.WrapError(err, "AccountSID") return } + // write "RequiredPrivileges" + err = en.Append(0xb2, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x73) + if err != nil { + return + } + err = en.WriteArrayHeader(uint32(len(z.RequiredPrivileges))) + if err != nil { + err = msgp.WrapError(err, "RequiredPrivileges") + return + } + for za0001 := range z.RequiredPrivileges { + err = en.WriteString(z.RequiredPrivileges[za0001]) + if err != nil { + err = msgp.WrapError(err, "RequiredPrivileges", za0001) + return + } + } return } // MarshalMsg implements msgp.Marshaler func (z *Service) MarshalMsg(b []byte) (o []byte, err error) { o = msgp.Require(b, z.Msgsize()) - // map header, size 13 + // map header, size 14 // string "RegistryOwner" - o = append(o, 0x8d, 0xad, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4f, 0x77, 0x6e, 0x65, 0x72) + o = append(o, 0x8e, 0xad, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4f, 0x77, 0x6e, 0x65, 0x72) o = msgp.AppendString(o, z.RegistryOwner) // string "RegistryDACL" o = append(o, 0xac, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x44, 0x41, 0x43, 0x4c) @@ -4976,6 +5012,12 @@ func (z *Service) MarshalMsg(b []byte) (o []byte, err error) { // string "AccountSID" o = append(o, 0xaa, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x49, 0x44) o = msgp.AppendString(o, z.AccountSID) + // string "RequiredPrivileges" + o = append(o, 0xb2, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x73) + o = msgp.AppendArrayHeader(o, uint32(len(z.RequiredPrivileges))) + for za0001 := range z.RequiredPrivileges { + o = msgp.AppendString(o, z.RequiredPrivileges[za0001]) + } return } @@ -5075,6 +5117,25 @@ func (z *Service) UnmarshalMsg(bts []byte) (o []byte, err error) { err = msgp.WrapError(err, "AccountSID") return } + case "RequiredPrivileges": + var zb0002 uint32 + zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "RequiredPrivileges") + return + } + if cap(z.RequiredPrivileges) >= int(zb0002) { + z.RequiredPrivileges = (z.RequiredPrivileges)[:zb0002] + } else { + z.RequiredPrivileges = make([]string, zb0002) + } + for za0001 := range z.RequiredPrivileges { + z.RequiredPrivileges[za0001], bts, err = msgp.ReadStringBytes(bts) + if err != nil { + err = msgp.WrapError(err, "RequiredPrivileges", za0001) + return + } + } default: bts, err = msgp.Skip(bts) if err != nil { @@ -5089,7 +5150,10 @@ func (z *Service) UnmarshalMsg(bts []byte) (o []byte, err error) { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *Service) Msgsize() (s int) { - s = 1 + 14 + msgp.StringPrefixSize + len(z.RegistryOwner) + 13 + msgp.BytesPrefixSize + len(z.RegistryDACL) + 5 + msgp.StringPrefixSize + len(z.Name) + 12 + msgp.StringPrefixSize + len(z.DisplayName) + 12 + msgp.StringPrefixSize + len(z.Description) + 10 + msgp.StringPrefixSize + len(z.ImagePath) + 16 + msgp.StringPrefixSize + len(z.ImageExecutable) + 21 + msgp.StringPrefixSize + len(z.ImageExecutableOwner) + 20 + msgp.BytesPrefixSize + len(z.ImageExecutableDACL) + 6 + msgp.IntSize + 5 + msgp.IntSize + 8 + msgp.StringPrefixSize + len(z.Account) + 11 + msgp.StringPrefixSize + len(z.AccountSID) + s = 1 + 14 + msgp.StringPrefixSize + len(z.RegistryOwner) + 13 + msgp.BytesPrefixSize + len(z.RegistryDACL) + 5 + msgp.StringPrefixSize + len(z.Name) + 12 + msgp.StringPrefixSize + len(z.DisplayName) + 12 + msgp.StringPrefixSize + len(z.Description) + 10 + msgp.StringPrefixSize + len(z.ImagePath) + 16 + msgp.StringPrefixSize + len(z.ImageExecutable) + 21 + msgp.StringPrefixSize + len(z.ImageExecutableOwner) + 20 + msgp.BytesPrefixSize + len(z.ImageExecutableDACL) + 6 + msgp.IntSize + 5 + msgp.IntSize + 8 + msgp.StringPrefixSize + len(z.Account) + 11 + msgp.StringPrefixSize + len(z.AccountSID) + 19 + msgp.ArrayHeaderSize + for za0001 := range z.RequiredPrivileges { + s += msgp.StringPrefixSize + len(z.RequiredPrivileges[za0001]) + } return } diff --git a/modules/query/types.go b/modules/query/types.go index 8c32be3..2831146 100644 --- a/modules/query/types.go +++ b/modules/query/types.go @@ -67,12 +67,15 @@ type QueryAnyAttribute struct { } func (qaa QueryAnyAttribute) Evaluate(o *engine.Object) bool { - for a, _ := range o.AttributeValueMap() { - if qaa.q.Evaluate(a, o) { - return true + var result bool + o.AttrIterator(func(attr engine.Attribute, avs engine.AttributeValues) bool { + if qaa.q.Evaluate(attr, o) { + result = true + return false // break } - } - return false + return true + }) + return result } func (qaa QueryAnyAttribute) ToLDAPFilter() string { @@ -263,12 +266,15 @@ func (lm lengthModifier) Evaluate(a engine.Attribute, o *engine.Object) bool { if !found { return lm.c.Compare(0, lm.value) } - for _, value := range vals.StringSlice() { - if lm.c.Compare(int64(len(value)), lm.value) { - return true + var result bool + vals.Iterate(func(value engine.AttributeValue) bool { + if lm.c.Compare(int64(len(value.String())), lm.value) { + result = true + return false } - } - return false + return true + }) + return result } func (lm lengthModifier) ToLDAPFilter(a string) string { diff --git a/modules/windowssecurity/getsecurity.go b/modules/windowssecurity/getsecurity.go index 20fa3f9..cdac8ca 100644 --- a/modules/windowssecurity/getsecurity.go +++ b/modules/windowssecurity/getsecurity.go @@ -46,7 +46,7 @@ func GetOwnerAndDACL(objectName string, objectType SE_OBJECT_TYPE) (SID, []byte, var oursid SID if err == nil { - oursid, err = SIDFromString(sid.String()) + oursid, err = ParseStringSID(sid.String()) } return oursid, dacl, err } diff --git a/modules/windowssecurity/sid.go b/modules/windowssecurity/sid.go index 8e9cfa0..ac13add 100644 --- a/modules/windowssecurity/sid.go +++ b/modules/windowssecurity/sid.go @@ -13,18 +13,23 @@ import ( "github.com/lkarlslund/adalanche/modules/ui" ) -type SID string +var ErrorOnlySIDVersion1Supported = errors.New("only SID version 1 supported") -const BlankSID = SID("") +type SID string -// 0 = revision +// Windows representation +// 0 = revision (always 1) // 1 = subauthority count // 2-7 = authority // 8-11+ = chunks of 4 with subauthorities -func ParseSID(data []byte) (SID, []byte, error) { +// Our representation +// 0-5 = authority +// 6-9+ = chunks of 4 with subauthorities + +func BytesToSID(data []byte) (SID, []byte, error) { if len(data) == 0 { - return SID(""), data, errors.New("No data supplied") + return "", data, errors.New("No data supplied") } if data[0] != 0x01 { if len(data) > 32 { @@ -37,10 +42,10 @@ func ParseSID(data []byte) (SID, []byte, error) { return "", data, errors.New("SID subauthority count is more than 15") } length := 8 + 4*subauthoritycount - return SID(dedup.D.BS(data[0:length])), data[length:], nil + return SID(dedup.D.BS(data[2:length])), data[length:], nil } -func SIDFromString(input string) (SID, error) { +func ParseStringSID(input string) (SID, error) { if len(input) < 5 { return "", errors.New("SID string is too short to be a SID") } @@ -51,7 +56,7 @@ func SIDFromString(input string) (SID, error) { if input[0] != 'S' { return "", errors.New("SID must start with S") } - var sid = make([]byte, 8+4*subauthoritycount) + var sid = make([]byte, 6+4*subauthoritycount) strnums := strings.Split(input, "-") @@ -59,8 +64,9 @@ func SIDFromString(input string) (SID, error) { if err != nil { return "", err } - sid[0] = byte(version) - sid[1] = byte(subauthoritycount) + if version != 1 { + return "", ErrorOnlySIDVersion1Supported + } authority, err := strconv.ParseInt(strnums[2], 10, 48) if err != nil { @@ -68,14 +74,14 @@ func SIDFromString(input string) (SID, error) { } authslice := make([]byte, 8) binary.BigEndian.PutUint64(authslice, uint64(authority)<<16) // dirty tricks - copy(sid[2:], authslice[0:6]) + copy(sid[0:], authslice[0:6]) for i := 0; i < subauthoritycount; i++ { subauthority, err := strconv.ParseUint(strnums[3+i], 10, 32) if err != nil { return "", err } - binary.LittleEndian.PutUint32(sid[8+4*i:], uint32(subauthority)) + binary.LittleEndian.PutUint32(sid[6+4*i:], uint32(subauthority)) } return SID(dedup.D.S(string(sid))), nil } @@ -89,13 +95,13 @@ func (sid SID) String() string { return "NULL SID" } var authority uint64 - for i := 2; i <= 7; i++ { + for i := 0; i <= 5; i++ { authority = authority<<8 | uint64(sid[i]) } - s := fmt.Sprintf("S-%d-%d", sid[0], authority) + s := fmt.Sprintf("S-1-%d", authority) // Subauthorities - for i := 8; i < len(sid); i += 4 { + for i := 6; i < len(sid); i += 4 { subauthority := binary.LittleEndian.Uint32([]byte(sid[i:])) s += fmt.Sprintf("-%d", subauthority) } @@ -112,13 +118,13 @@ func (sid *SID) UnmarshalJSON(data []byte) error { if err != nil { return err } - newsid, err := SIDFromString(sidstring) + newsid, err := ParseStringSID(sidstring) *sid = newsid return err } func (sid SID) Components() int { - return len(sid) / 4 + return (len(sid) + 2) / 4 } func (sid SID) Component(n int) uint64 { @@ -127,19 +133,19 @@ func (sid SID) Component(n int) uint64 { if len(sid) == 0 { return 0 // FAIL } - return uint64(sid[0]) + return 1 // always version 1 case 1: if len(sid) < 8 { return 0 // FAIL } var authority uint64 - for i := 2; i <= 7; i++ { + for i := 0; i <= 5; i++ { authority = authority<<8 | uint64(sid[i]) } return authority default: - offset := n * 4 + offset := n*4 - 2 if len(sid) < offset+3 { return 0 // FAIL } @@ -148,24 +154,25 @@ func (sid SID) Component(n int) uint64 { } func (sid SID) StripRID() SID { - if len(sid) < 12 { + if len(sid) < 10 { ui.Error().Msgf("SID %s is too short to strip RID", sid) return "" } - newsid := make([]byte, len(sid)-4) - copy(newsid, sid) - newsid[1] = byte(len(newsid)/4) - 2 // Adjust internal length - return SID(newsid) + return sid[:len(sid)-4] } func (sid SID) RID() uint32 { - if len(sid) <= 8 { + if len(sid) <= 6 { return 0 } l := len(sid) - 4 return binary.LittleEndian.Uint32([]byte(sid[l:])) } +func (sid SID) IsBlank() bool { + return sid == "" +} + func (sid SID) AddComponent(component uint32) SID { newsid := make([]byte, len(sid)+4) copy(newsid, sid) @@ -174,14 +181,14 @@ func (sid SID) AddComponent(component uint32) SID { return SID(newsid) } -func SIDFromBytes(data uintptr) (SID, error) { +func SIDFromPtr(data uintptr) (SID, error) { bytes := (*[1024]byte)(unsafe.Pointer(data)) if bytes[0] != 0x01 { return "", fmt.Errorf("SID revision must be 1 (dump %x ...)", bytes[0:32]) } subauthoritycount := int(bytes[1]) - var sid = make([]byte, 8+4*subauthoritycount) + var sid = make([]byte, 6+4*subauthoritycount) - copy(sid, bytes[0:len(sid)]) + copy(sid, bytes[2:len(sid)]) return SID(sid), nil } diff --git a/modules/windowssecurity/wellknown.go b/modules/windowssecurity/wellknown.go index 2758fe5..fc922df 100644 --- a/modules/windowssecurity/wellknown.go +++ b/modules/windowssecurity/wellknown.go @@ -80,19 +80,21 @@ var ( "S-1-5-90-0": "Windows Manager - Windows Manager Group", } - AdministratorsSID, _ = SIDFromString("S-1-5-32-544") - RemoteDesktopUsersSID, _ = SIDFromString("S-1-5-32-555") - DCOMUsersSID, _ = SIDFromString("S-1-5-32-562") + AdministratorsSID, _ = ParseStringSID("S-1-5-32-544") + RemoteDesktopUsersSID, _ = ParseStringSID("S-1-5-32-555") + DCOMUsersSID, _ = ParseStringSID("S-1-5-32-562") - OwnerSID, _ = SIDFromString("S-1-3-4") - SystemSID, _ = SIDFromString("S-1-5-18") - CreatorOwnerSID, _ = SIDFromString("S-1-3-0") - SelfSID, _ = SIDFromString("S-1-5-10") - AuthenticatedUsersSID, _ = SIDFromString("S-1-5-11") - EveryoneSID, _ = SIDFromString("S-1-1-0") - ServicesSID, _ = SIDFromString("S-1-5-6") - NetworkServiceSID, _ = SIDFromString("S-1-5-19") - LocalServiceSID, _ = SIDFromString("S-1-5-20") + OwnerSID, _ = ParseStringSID("S-1-3-4") + CreatorOwnerSID, _ = ParseStringSID("S-1-3-0") + SelfSID, _ = ParseStringSID("S-1-5-10") + AuthenticatedUsersSID, _ = ParseStringSID("S-1-5-11") + EveryoneSID, _ = ParseStringSID("S-1-1-0") - AccountOperatorsSID, _ = SIDFromString("S-1-5-32-548") + ServicesSID, _ = ParseStringSID("S-1-5-6") + + SystemSID, _ = ParseStringSID("S-1-5-18") + LocalServiceSID, _ = ParseStringSID("S-1-5-19") + NetworkServiceSID, _ = ParseStringSID("S-1-5-20") + + AccountOperatorsSID, _ = ParseStringSID("S-1-5-32-548") )