From ffc9f9dfb9c914ef56bd2274bac50c4b1f0d2adf Mon Sep 17 00:00:00 2001 From: Lars Karlslund Date: Tue, 25 Oct 2022 12:48:15 +0200 Subject: [PATCH] Many, many internal changes Got rid of Slice() on AttributeValues, fixed race in edge statistics, fixed attribute value bug in Absorb(), Moved some Object methods to Edges(direction), changed Attribute type from int16 to uint16, AttributeValueMap supports pre-allocation if we switch to a map type that supports it, EdgeConnections points to IDs not Objects, created a weak ID -> Object slice tracker, made everything thread safe all the time (LOL, at least that's the ambition), added cache for AttributeValueToIndex to minimize ToLower calls, added Iterate and IterateParallel to Objects, --- go.mod | 10 +- go.sum | 58 +--- modules/analyze/webservicefuncs.go | 2 +- modules/engine/analyzeobjects.go | 2 +- modules/engine/analyzepaths.go | 19 +- modules/engine/attributenode.go | 25 -- modules/engine/attributes.go | 12 +- modules/engine/attributevalue.go | 32 +-- modules/engine/attributevaluemap.go | 71 +++-- modules/engine/edge.go | 45 +-- modules/engine/edgeconnections.go | 104 +++++++ modules/engine/flexlock.go | 43 --- modules/engine/graph.go | 6 +- modules/engine/object.go | 272 ++++++++---------- modules/engine/objectindex.go | 50 ++++ modules/engine/objects.go | 130 +++++---- modules/engine/processing.go | 6 + modules/engine/pwnanalyzers.go | 5 +- modules/engine/run.go | 8 - .../activedirectory/analyze/adloader.go | 4 - .../activedirectory/analyze/analyze-ad.go | 55 ++-- .../activedirectory/analyze/gpoloader.go | 2 - .../integrations/activedirectory/rawobject.go | 5 +- .../localmachine/analyze/analyzer.go | 2 +- .../localmachine/analyze/loader.go | 2 - .../localmachine/analyze/postprocessing.go | 2 +- modules/query/types.go | 28 +- 27 files changed, 505 insertions(+), 495 deletions(-) delete mode 100644 modules/engine/attributenode.go create mode 100644 modules/engine/edgeconnections.go delete mode 100644 modules/engine/flexlock.go create mode 100644 modules/engine/objectindex.go diff --git a/go.mod b/go.mod index 3b265fa..36ce0ab 100644 --- a/go.mod +++ b/go.mod @@ -7,8 +7,6 @@ require ( github.com/OneOfOne/xxhash v1.2.8 github.com/SaveTheRbtz/generic-sync-map-go v0.0.0-20220414055132-a37292614db8 github.com/Showmax/go-fqdn v1.0.0 - github.com/absfs/gofs v0.0.0-20210326223041-415ec8094056 - github.com/absfs/osfs v0.0.0-20210816191758-403afc5396f8 github.com/amidaware/taskmaster v0.0.0-20220111015025-c9cd178bbbf2 github.com/antchfx/xmlquery v1.3.12 github.com/gin-gonic/gin v1.7.7 @@ -45,7 +43,6 @@ require ( atomicgo.dev/cursor v0.1.1 // indirect atomicgo.dev/keyboard v0.2.8 // indirect github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect - github.com/absfs/absfs v0.0.0-20200602175035-e49edc9fef15 // indirect github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect github.com/antchfx/xpath v1.2.1 // indirect github.com/asergeyev/nradix v0.0.0-20170505151046-3872ab85bb56 // indirect @@ -108,11 +105,14 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect ) -require github.com/felixge/fgtrace v0.2.0 +require ( + github.com/akyoto/cache v1.0.6 + github.com/elastic/go-windows v1.0.1 + github.com/felixge/fgtrace v0.2.0 +) require ( github.com/DataDog/gostackparse v0.6.0 // indirect - github.com/elastic/go-windows v1.0.1 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/tools v0.1.12 // indirect ) diff --git a/go.sum b/go.sum index 75c98cb..8027fe4 100644 --- a/go.sum +++ b/go.sum @@ -52,9 +52,6 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= collectd.org v0.3.1-0.20181025072142-f80706d1e115/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= -github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e h1:ZU22z/2YRFLyf/P4ZwUYSdNCWsMEI0VeyrFoI2rAhJQ= -github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -71,8 +68,6 @@ github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzX github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c= github.com/MarvinJWendt/testza v0.4.2 h1:Vbw9GkSB5erJI2BPnBL9SVGV9myE+XmUSFahBGUhW2Q= github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE= -github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -87,16 +82,8 @@ github.com/Showmax/go-fqdn v1.0.0 h1:0rG5IbmVliNT5O19Mfuvna9LL7zlHyRfsSvBPZmF9tM github.com/Showmax/go-fqdn v1.0.0/go.mod h1:SfrFBzmDCtCGrnHhoDjuvFnKsWjEQX/Q9ARZvOrJAko= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/StackExchange/wmi v1.2.0/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/absfs/absfs v0.0.0-20200602175035-e49edc9fef15 h1:tcUuSvytlUEjm5D1qu7beKnaPf/uWtNXVutHxXqVJ6A= -github.com/absfs/absfs v0.0.0-20200602175035-e49edc9fef15/go.mod h1:EcuvbVuyyWyu+g4ACjKzyUypG60qSvorqC/hjByBEqY= -github.com/absfs/fstesting v0.0.0-20180810212821-8b575cdeb80d h1:EVkAQkoP/iYX7WpkSgaSkHr5AgDdzxR06Hmy+bu4YpU= -github.com/absfs/fstesting v0.0.0-20180810212821-8b575cdeb80d/go.mod h1:Ib9xUBFJeggV+KCP6/90/ymnt4Siu6V1vBFJrrT1y/s= -github.com/absfs/gofs v0.0.0-20210326223041-415ec8094056 h1:RlFbh4zQyOul6E7pSw9jjfhNAVbQKN3lDQiMKpv2688= -github.com/absfs/gofs v0.0.0-20210326223041-415ec8094056/go.mod h1:4FsRWqM9calJ0EcC7eXdYiAk5UM+g7ZmddbbnnMMdyo= -github.com/absfs/inode v0.0.0-20190804195220-b7cd14cdd0dc/go.mod h1:lc9vQxyCSyrjclBTgazRvacmElJLj7VfpDmgDnL77o0= -github.com/absfs/memfs v0.0.0-20190429000831-6a320d582782/go.mod h1:ZF+JG90ujrBa3hNBmMihd6HEB8ALIL+u0D2qfKY5d9M= -github.com/absfs/osfs v0.0.0-20210816191758-403afc5396f8 h1:DX00H7BAKot581+cWgL8ZnIMTMaLFwuO4qDmBYvGWRE= -github.com/absfs/osfs v0.0.0-20210816191758-403afc5396f8/go.mod h1:IIzwVILCbb3j0VHjcAQ7Xwpdz1h57eUzZil7DCIel/c= +github.com/akyoto/cache v1.0.6 h1:5XGVVYoi2i+DZLLPuVIXtsNIJ/qaAM16XT0LaBaXd2k= +github.com/akyoto/cache v1.0.6/go.mod h1:WfxTRqKhfgAG71Xh6E3WLpjhBtZI37O53G4h5s+3iM4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -105,12 +92,8 @@ github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3Uu github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/amidaware/taskmaster v0.0.0-20220111015025-c9cd178bbbf2 h1:1K03qwtvgdJRXJr0nE1qvzFPOmbWHBnnnbeblU7+Bg8= github.com/amidaware/taskmaster v0.0.0-20220111015025-c9cd178bbbf2/go.mod h1:5UVBogOiPFWC2F6fKT/Kb9qD4NCsp2y+dCj+pvXnDQE= -github.com/antchfx/xmlquery v1.3.9 h1:Y+zyMdiUZ4fasTQTkDb3DflOXP7+obcYEh80SISBmnQ= -github.com/antchfx/xmlquery v1.3.9/go.mod h1:wojC/BxjEkjJt6dPiAqUzoXO5nIMWtxHS8PD8TmN4ks= github.com/antchfx/xmlquery v1.3.12 h1:6TMGpdjpO/P8VhjnaYPXuqT3qyJ/VsqoyNTmJzNBTQ4= github.com/antchfx/xmlquery v1.3.12/go.mod h1:3w2RvQvTz+DaT5fSgsELkSJcdNgkmg6vuXDEuhdwsPQ= -github.com/antchfx/xpath v1.2.0 h1:mbwv7co+x0RwgeGAOHdrKy89GvHaGvxxBtPK0uF9Zr8= -github.com/antchfx/xpath v1.2.0/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/antchfx/xpath v1.2.1 h1:qhp4EW6aCOVr5XIkT+l6LJ9ck/JsUH/yyauNgTQkBF8= github.com/antchfx/xpath v1.2.1/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -193,8 +176,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/fgtrace v0.2.0 h1:lq7RO6ELjR+S74+eD+ai/vhYvsjno7Vb84yzU6RPSeU= github.com/felixge/fgtrace v0.2.0/go.mod h1:q9vMuItthu3CRfNhirTCTwzBcJ8atUFkrJUhgQbjg8c= @@ -209,9 +190,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= -github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-asn1-ber/asn1-ber v1.5.3 h1:u7utq56RUFiynqUzgVMFDymapcOtQ/MZkh3H4QYkxag= -github.com/go-asn1-ber/asn1-ber v1.5.3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -311,8 +289,7 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-write v0.0.0-20181107114627-56629a6b2542 h1:jCpVy/nfZ7ayHSZe3xdDhYy6TftqehkNU6hh8Kq+iW8= github.com/google/go-write v0.0.0-20181107114627-56629a6b2542/go.mod h1:NOSj1rhiMiScdUd1ere2UGAG2ZrYdyblYixNPWPlP5w= @@ -455,12 +432,6 @@ github.com/lkarlslund/binstruct v1.3.1-0.20220418073417-7618823b3136 h1:+Kk6n+RA github.com/lkarlslund/binstruct v1.3.1-0.20220418073417-7618823b3136/go.mod h1:md9fQkOXb+vqf3U+SVAsNfzLvKFK1Q+MJLj2v2sBj+4= github.com/lkarlslund/go-win64api v0.0.0-20211005130710-d4f2d07ed091 h1:6y0mbZnTDDZ8pOl3C/8W8j0a6OGuxNWxU07iZeBRpYg= github.com/lkarlslund/go-win64api v0.0.0-20211005130710-d4f2d07ed091/go.mod h1:JFCoCajhTrJw5YFNm/Xm0o+lt5jhY974icELnvnVFdc= -github.com/lkarlslund/ldap/v3 v3.2.4-0.20210621153959-85555023df29 h1:4fuGT7A0TbnV/70EOC04T8rMGkovrsgaqgZK2d2CKy0= -github.com/lkarlslund/ldap/v3 v3.2.4-0.20210621153959-85555023df29/go.mod h1:Q/0KZGCdyOiOp6oN+HFifXw9rwBwVB6NkNL4tXXDF70= -github.com/lkarlslund/ldap/v3 v3.2.4-0.20211222054708-7db51dc0ada6 h1:OzH723T8Vcd2uO52zqrCQPQpipqwt+g53eCQi5Wz2sA= -github.com/lkarlslund/ldap/v3 v3.2.4-0.20211222054708-7db51dc0ada6/go.mod h1:XAMlidLIIWLdsI6XiERrxgAwMXk2aDh6U0cCAIVWHXE= -github.com/lkarlslund/ldap/v3 v3.2.4-0.20220924044144-91eb9f07f0b7 h1:zIJFy3dAVAGUTfpR7qBNoDRweCMeskMHjbJoc10J/u8= -github.com/lkarlslund/ldap/v3 v3.2.4-0.20220924044144-91eb9f07f0b7/go.mod h1:0wE7xreiGneIlOw9d76AykHNfq/8lNpV8ZO0A/2xnmc= github.com/lkarlslund/ldap/v3 v3.4.4-1 h1:dcVlqedaugh0UtxgvgHMn83g4ARrmCupHvJiJ7SQb4c= github.com/lkarlslund/ldap/v3 v3.4.4-1/go.mod h1:0wE7xreiGneIlOw9d76AykHNfq/8lNpV8ZO0A/2xnmc= github.com/lkarlslund/stringdedup v0.6.2 h1:IcoGuXAuZxjntVnxTi7/C+RFh+gVc5wCyt4gkHp9FxA= @@ -478,7 +449,6 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -521,7 +491,6 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs= github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/open-networks/go-msgraph v0.0.0-20200217121338-a7bf31e9c1f2/go.mod h1:qrAWeYL/1D4WyM9vJnTmgKMtspVODwsvgBDXgVs64Gg= @@ -569,11 +538,9 @@ github.com/pterm/pterm v0.12.45 h1:5HATKLTDjl9D74b0x7yiHzFI7OADlSXK3yHrJNhRwZE= github.com/pterm/pterm v0.12.45/go.mod h1:hJgLlBafm45w/Hr0dKXxY//POD7CgowhePaG1sdPNBg= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rickb777/date v1.14.2 h1:PCme7ZL/cniZmDgS9Pyn5fHmu5A6lz12Ibfd33FmDiw= github.com/rickb777/date v1.14.2/go.mod h1:swmf05C+hN+m8/Xh7gEq3uB6QJDNc5pQBWojKdHetOs= github.com/rickb777/date v1.20.0 h1:oRGcq4b+ba12N/HnsVZuWSK/QJb/o/hnjOJEyRMGUT0= github.com/rickb777/date v1.20.0/go.mod h1:8AR0TBrjDGUjwKToBI8L+RafzNg7gqlT0ox0cERCwEo= -github.com/rickb777/plural v1.2.2 h1:4CU5NiUqXSM++2+7JCrX+oguXd2D7RY5O1YisMw1yCI= github.com/rickb777/plural v1.2.2/go.mod h1:xyHbelv4YvJE51gjMnHvk+U2e9zIysg6lTnSQK8XUYA= github.com/rickb777/plural v1.4.1 h1:5MMLcbIaapLFmvDGRT5iPk8877hpTPt8Y9cdSKRw9sU= github.com/rickb777/plural v1.4.1/go.mod h1:kdmXUpmKBJTS0FtG/TFumd//VBWsNTD7zOw7x4umxNw= @@ -600,13 +567,10 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil v2.20.9+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v3 v3.22.2 h1:wCrArWFkHYIdDxx/FSfF5RB4dpJYW6t7rcp3+zL8uks= -github.com/shirou/gopsutil/v3 v3.22.2/go.mod h1:WapW1AOOPlHyXr+yOyw3uYx36enocrtSoSBy0L5vUHY= github.com/shirou/gopsutil/v3 v3.22.8 h1:a4s3hXogo5mE2PfdfJIonDbstO/P+9JszdfhAHSzD9Y= github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= @@ -627,19 +591,16 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM= github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw= github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= -github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= -github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= github.com/turnage/graw v0.0.0-20191104042329-405cc3092119/go.mod h1:mCzFVBigviR4gb9WRHCFEZ4Z8eWB1dGz+fzLOHpkG8I= @@ -654,8 +615,6 @@ github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhe github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= -github.com/xtgo/set v1.0.0 h1:6BCNBRv3ORNDQ7fyoJXRv+tstJz3m1JVFQErfeZz2pY= -github.com/xtgo/set v1.0.0/go.mod h1:d3NHzGzSa0NmB2NhFyECA+QdRp29oEn2xbT+TpeFoM8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -691,15 +650,12 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 h1:syTAU9FwmvzEoIYMqcPHOcVm4H3U5u90WsvuYgwpETU= -golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7 h1:WJywXQVIb56P2kAvXeMGTIgQ1ZHQxR60+F9dLsodECc= golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -772,7 +728,6 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -895,7 +850,6 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -905,7 +859,6 @@ golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -995,7 +948,6 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -1175,8 +1127,8 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/modules/analyze/webservicefuncs.go b/modules/analyze/webservicefuncs.go index 3bb61fb..521156d 100644 --- a/modules/analyze/webservicefuncs.go +++ b/modules/analyze/webservicefuncs.go @@ -858,7 +858,7 @@ func analysisfuncs(ws *webservice) { var pwnlinks int for _, object := range ws.Objs.Slice() { - pwnlinks += object.EdgeCount(engine.Out) + pwnlinks += object.Edges(engine.Out).Len() } result.Statistics["Total"] = len(ws.Objs.Slice()) result.Statistics["PwnConnections"] = pwnlinks diff --git a/modules/engine/analyzeobjects.go b/modules/engine/analyzeobjects.go index 308ca96..d3bc215 100644 --- a/modules/engine/analyzeobjects.go +++ b/modules/engine/analyzeobjects.go @@ -123,7 +123,7 @@ func AnalyzeObjects(opts AnalyzeObjectsOptions) (pg Graph) { // Iterate over ever outgoing pwn // This is not efficient, but we sort the pwnlist first - object.EdgeIterator(ec, func(target *Object, eb EdgeBitmap) bool { + object.Edges(ec).Range(func(target *Object, eb EdgeBitmap) bool { // If this is not a chosen method, skip it detectededges := eb.Intersect(detectedges) diff --git a/modules/engine/analyzepaths.go b/modules/engine/analyzepaths.go index c9d8dcf..dc6c44d 100644 --- a/modules/engine/analyzepaths.go +++ b/modules/engine/analyzepaths.go @@ -148,6 +148,8 @@ func AnalyzePaths(start, end *Object, obs *Objects, lookforedges EdgeBitmap, min dist := make(map[*Object]uint32) prev := make(map[*Object]*Object) + opeb := make(map[ObjectPair]EdgeBitmap) + q := heapqueue{} // q := queue{} @@ -166,7 +168,7 @@ func AnalyzePaths(start, end *Object, obs *Objects, lookforedges EdgeBitmap, min visited[source] = struct{}{} - source.EdgeIterator(Out, func(target *Object, edges EdgeBitmap) bool { + source.Edges(Out).Range(func(target *Object, edges EdgeBitmap) bool { if _, found := visited[target]; !found { // If this is not a chosen method, skip it detectededges := edges.Intersect(lookforedges) @@ -182,6 +184,12 @@ func AnalyzePaths(start, end *Object, obs *Objects, lookforedges EdgeBitmap, min return true //continue } + // Save for later + opeb[ObjectPair{ + Source: source, + Target: target, + }] = edges + weight := uint32(101 - prob) sdist, sfound := dist[source] @@ -225,9 +233,12 @@ func AnalyzePaths(start, end *Object, obs *Objects, lookforedges EdgeBitmap, min }) result.Connections = append(result.Connections, GraphEdge{ - Source: prenode, - Target: curnode, - EdgeBitmap: prenode.Edge(Out, curnode), + Source: prenode, + Target: curnode, + EdgeBitmap: opeb[ObjectPair{ + Source: prenode, + Target: curnode, + }], }) if prenode == start { break diff --git a/modules/engine/attributenode.go b/modules/engine/attributenode.go deleted file mode 100644 index 5cbbac3..0000000 --- a/modules/engine/attributenode.go +++ /dev/null @@ -1,25 +0,0 @@ -package engine - -// AttributeNode can contain children -type AttributeNode interface { - Children() AttributeValueMap - ChildrenLen() int - ChildrenSlice() []AttributeAndValues -} - -// type AttributeValueWithChildren struct { -// AttributeValue -// data AttributeValueMap -// } - -// func (avwc AttributeValueWithChildren) Children() AttributeValueMap { -// return nil -// } - -// func (avwc AttributeValueWithChildren) ChildrenLen() int { -// return 0 -// } - -// func (avwc AttributeValueWithChildren) ChildrenSlice() []AttributeAndValues { -// return nil -// } diff --git a/modules/engine/attributes.go b/modules/engine/attributes.go index b9d21fd..e549f3b 100644 --- a/modules/engine/attributes.go +++ b/modules/engine/attributes.go @@ -14,6 +14,8 @@ type AttributeGetFunc func(o *Object, a Attribute) (v AttributeValues, found boo type AttributeSetFunc func(o *Object, a Attribute, v AttributeValues) error type attributeinfo struct { + onset AttributeSetFunc + onget AttributeGetFunc name string tags []string atype AttributeType @@ -21,8 +23,6 @@ type attributeinfo struct { unique bool // Doing a Find on this attribute will return multiple results merge bool // If true, objects can be merged on this attribute hidden bool // If true this does not show up in the list of attributes - onset AttributeSetFunc - onget AttributeGetFunc } type AttributeType uint8 @@ -49,7 +49,7 @@ var mergeapprovers []mergeapproverinfo var attributenums []attributeinfo var ( - NonExistingAttribute = Attribute(-1) + NonExistingAttribute = ^Attribute(0) DistinguishedName = NewAttribute("distinguishedName").Single().Unique() ObjectClass = NewAttribute("objectClass") @@ -101,12 +101,12 @@ var ( MetaLAPSInstalled = NewAttribute("_haslaps") ) -type Attribute int16 +type Attribute uint16 var attributemutex sync.RWMutex func NewAttribute(name string) Attribute { - if name[len(name)-1] >= '0' && name[len(name)-1] <= '9' && strings.Index(name, ";") != -1 { + if name[len(name)-1] >= '0' && name[len(name)-1] <= '9' && strings.Contains(name, ";") { if !strings.HasPrefix(name, "member;") { ui.Debug().Msgf("Incomplete data detected in attribute %v", name) } @@ -150,7 +150,7 @@ func NewAttribute(name string) Attribute { } func (a Attribute) String() string { - if a == -1 { + if a == NonExistingAttribute { return "N/A" } return attributenums[a].name diff --git a/modules/engine/attributevalue.go b/modules/engine/attributevalue.go index 3ab11d1..3d004a5 100644 --- a/modules/engine/attributevalue.go +++ b/modules/engine/attributevalue.go @@ -8,7 +8,6 @@ import ( "time" "github.com/gofrs/uuid" - "github.com/lkarlslund/adalanche/modules/ui" "github.com/lkarlslund/adalanche/modules/windowssecurity" ) @@ -71,7 +70,6 @@ type AttributeAndValues struct { type AttributeValues interface { First() AttributeValue Iterate(func(val AttributeValue) bool) - Slice() []AttributeValue StringSlice() []string Len() int } @@ -112,18 +110,10 @@ func (avs AttributeValueSlice) Iterate(it func(val AttributeValue) bool) { } } -func (avs AttributeValueSlice) Slice() []AttributeValue { - return avs -} - func (avs AttributeValueSlice) StringSlice() []string { result := make([]string, len(avs)) for i := 0; i < len(avs); i++ { - if avs[i] == nil { - ui.Warn().Msg("Encountered NIL value") - } else { - result[i] = avs[i].String() - } + result[i] = avs[i].String() } return result } @@ -148,11 +138,6 @@ func (avo AttributeValueOne) Len() int { return 1 } -// This is really killing us -func (avo AttributeValueOne) Slice() []AttributeValue { - return AttributeValueSlice{avo.Value} -} - func (avo AttributeValueOne) StringSlice() []string { return []string{avo.Value.String()} } @@ -161,7 +146,6 @@ type AttributeValue interface { String() string Raw() interface{} IsZero() bool - // Compare(other AttributeValue) bool } type AttributeValueObject struct { @@ -283,17 +267,3 @@ func (as AttributeValueGUID) Raw() interface{} { func (as AttributeValueGUID) IsZero() bool { return uuid.UUID(as).IsNil() } - -// type AttributeValueFiletime []byte - -// func (as AttributeValueFiletime) String() string { -// return string(as) -// } - -// func (as AttributeValueFiletime) Raw() interface{} { -// return string(as) -// } - -// func (as AttributeValueFiletime) AsTime() time.Time { -// return nil -// } diff --git a/modules/engine/attributevaluemap.go b/modules/engine/attributevaluemap.go index 67d0083..f7b03a7 100644 --- a/modules/engine/attributevaluemap.go +++ b/modules/engine/attributevaluemap.go @@ -1,38 +1,67 @@ package engine +import ( + gsync "github.com/SaveTheRbtz/generic-sync-map-go" +) + type AttributeValueMap struct { - m map[Attribute]AttributeValues + // m *xsync.MapOf[Attribute, AttributeValues] + // m map[Attribute]AttributeValues + // m *haxmap.Map[Attribute, AttributeValues] + m gsync.MapOf[Attribute, AttributeValues] +} + +func (avm *AttributeValueMap) init(preloadAttributes int) { + // avm.m = haxmap.New[Attribute, AttributeValues](1) + // avm.m = make(map[Attribute]AttributeValues) + // avm.m = xsync.NewTypedMapOf[Attribute, AttributeValues](func(a Attribute) uint64 { + // return uint64(a) + // }) } -func (avm AttributeValueMap) Get(a Attribute) (av AttributeValues, found bool) { - if avm.m == nil { - return nil, false - } - av, found = avm.m[a] +func (avm *AttributeValueMap) Get(a Attribute) (av AttributeValues, found bool) { + // av, found = avm.m.Get(a) + // if found && av.Len() == 0 { + // found = false // workaround until haxmap performance for deletes is fixed + // } + // av, found = avm.m[a] + av, found = avm.m.Load(a) return } func (avm *AttributeValueMap) Set(a Attribute, av AttributeValues) { - if avm.m == nil { - avm.m = make(map[Attribute]AttributeValues) - } - avm.m[a] = av + // avm.m.Set(a, av) + // avm.m[a] = av + avm.m.Store(a, av) } -func (avm AttributeValueMap) Len() int { - return len(avm.m) +func (avm *AttributeValueMap) Len() int { + var count int + avm.m.Range(func(u Attribute, av AttributeValues) bool { + // if av.Len() > 0 { + count++ + // } + return true + }) + return count + // return len(avm.m) + // return avm.m.Size() } func (avm *AttributeValueMap) Clear(a Attribute) { - if avm.m != nil { - delete(avm.m, a) - } + // avm.m.Set(a, NoValues{}) // Workaround until haxmap performance + // delete(avm.m, a) + avm.m.Delete(a) } -func (avm AttributeValueMap) Iterate(f func(attr Attribute, values AttributeValues) bool) { - for attr, values := range avm.m { - if !f(attr, values) { - break - } - } +func (avm *AttributeValueMap) Iterate(f func(attr Attribute, values AttributeValues) bool) { + avm.m.Range(f) + // for a, av := range avm.m { + // if !f(a, av) { + // break + // } + // } + // avm.m.Range(func(a Attribute, av AttributeValues) bool { + // return f(a, av) + // }) } diff --git a/modules/engine/edge.go b/modules/engine/edge.go index 80ee055..72d7486 100644 --- a/modules/engine/edge.go +++ b/modules/engine/edge.go @@ -2,9 +2,9 @@ package engine import ( "math/bits" - "sort" "strings" "sync" + "sync/atomic" ) type ProbabilityCalculatorFunction func(source, target *Object) Probability @@ -53,7 +53,7 @@ type EdgeInfo struct { } func (eb EdgeBitmap) Set(edge Edge) EdgeBitmap { - EdgePopularity[edge]++ + atomic.AddUint64(&EdgePopularity[edge], 1) return eb.set(edge) } @@ -116,22 +116,6 @@ func (eb EdgeBitmap) Edges() []Edge { return result } -func (ec EdgeConnections) Objects() ObjectSlice { - result := make(ObjectSlice, len(ec)) - var i int - for object := range ec { - result[i] = object - i++ - } - sort.Sort(result) - return result -} - -func (ec EdgeConnections) Set(o *Object, edge Edge) { - p := ec[o] - ec[o] = p.Set(edge) -} - type Edge int var edgeMutex sync.RWMutex @@ -139,10 +123,10 @@ var edgeNames = make(map[string]Edge) var edgeInfos []*edgeInfo type edgeInfo struct { + probability ProbabilityCalculatorFunction Name string Description string tags []string - probability ProbabilityCalculatorFunction multi bool // If true, this attribute can have multiple values nonunique bool // Doing a Find on this attribute will return multiple results merge bool // If true, objects can be merged on this attribute @@ -268,29 +252,6 @@ const ( In EdgeDirection = 1 ) -type EdgeConnections map[*Object]EdgeBitmap - -var globalEdgeConnectionsLock sync.Mutex // Ugly but it will do - -func (ec EdgeConnections) StringMap() map[string]string { - result := make(map[string]string) - for o, eb := range ec { - result[o.String()] = eb.JoinedString() - } - return result -} - -// Thread safe range -func (ec EdgeConnections) Range(rf func(*Object, EdgeBitmap) bool) { - globalEdgeConnectionsLock.Lock() - for o, eb := range ec { - if !rf(o, eb) { - break - } - } - globalEdgeConnectionsLock.Unlock() -} - func (m EdgeBitmap) IsSet(method Edge) bool { return (m[method/64] & (1 << (method % 64))) != 0 // Uuuuh, nasty and unreadable } diff --git a/modules/engine/edgeconnections.go b/modules/engine/edgeconnections.go new file mode 100644 index 0000000..61ae846 --- /dev/null +++ b/modules/engine/edgeconnections.go @@ -0,0 +1,104 @@ +package engine + +import ( + "sort" + "sync" + + gsync "github.com/SaveTheRbtz/generic-sync-map-go" +) + +type EdgeConnections struct { + ecm gsync.MapOf[uint32, EdgeBitmap] + lock sync.Mutex // For stable modification of edges + // m *haxmap.Map[unsafe.Pointer, EdgeBitmap] +} + +var globalEdgeConnectionsLock sync.Mutex // Ugly but it will do + +func (ec *EdgeConnections) StringMap() map[string]string { + result := make(map[string]string) + ec.RangeID(func(id uint32, eb EdgeBitmap) bool { + result[IDtoOBject(id).Label()] = eb.JoinedString() + return true + }) + return result +} + +// Thread safe range +func (ec *EdgeConnections) Range(rf func(*Object, EdgeBitmap) bool) { + ec.ecm.Range(func(id uint32, eb EdgeBitmap) bool { + return rf(IDtoOBject(id), eb) + }) +} + +func (ec *EdgeConnections) RangeID(rf func(uint32, EdgeBitmap) bool) { + ec.ecm.Range(func(id uint32, eb EdgeBitmap) bool { + return rf(id, eb) + }) +} + +func (ec *EdgeConnections) Len() int { + var count int + ec.RangeID(func(id uint32, eb EdgeBitmap) bool { + count++ + return true + }) + return count +} + +func (ec *EdgeConnections) Objects() ObjectSlice { + result := make(ObjectSlice, ec.Len()) + var i int + ec.Range(func(o *Object, eb EdgeBitmap) bool { + result[i] = o + i++ + return true + }) + sort.Sort(result) + return result +} + +// func (ec *EdgeConnections) Get(o *Object) (EdgeBitmap, bool) { +// return ec.m.Load(o.ID()) +// } + +func (ec *EdgeConnections) GetOrSet(o *Object, eb EdgeBitmap) (EdgeBitmap, bool) { + ec.lock.Lock() + res, status := ec.ecm.LoadOrStore(o.ID(), eb) + ec.lock.Unlock() + return res, status +} + +func (ec *EdgeConnections) Set(o *Object, edge EdgeBitmap) { + ec.lock.Lock() + ec.ecm.Store(o.ID(), edge) + ec.lock.Unlock() +} + +var GlobalFSPartOfGPO uint32 + +func (ec *EdgeConnections) SetEdge(o *Object, edge Edge) { + ec.lock.Lock() + p, _ := ec.ecm.Load(o.ID()) + newedge := p.Set(edge) + ec.ecm.Store(o.ID(), newedge) + ec.lock.Unlock() +} + +func (ec *EdgeConnections) ClearEdge(o *Object, edge Edge) { + ec.lock.Lock() + p, loaded := ec.ecm.Load(o.ID()) + if !loaded { + ec.lock.Unlock() + return + } + newedge := p.Clear(edge) + ec.ecm.Store(o.ID(), newedge) + ec.lock.Unlock() +} + +func (ec *EdgeConnections) Del(o *Object) { + ec.lock.Lock() + ec.ecm.Delete(o.ID()) + ec.lock.Unlock() +} diff --git a/modules/engine/flexlock.go b/modules/engine/flexlock.go deleted file mode 100644 index 7afb8e3..0000000 --- a/modules/engine/flexlock.go +++ /dev/null @@ -1,43 +0,0 @@ -package engine - -import "sync" - -type FlexMutex struct { - m sync.RWMutex - enabled uint64 -} - -func (fm *FlexMutex) Lock() { - if fm.enabled != 0 { - fm.m.Lock() - } -} - -func (fm *FlexMutex) Unlock() { - if fm.enabled != 0 { - fm.m.Unlock() - } -} - -func (fm *FlexMutex) RLock() { - if fm.enabled != 0 { - fm.m.RLock() - } -} - -func (fm *FlexMutex) RUnlock() { - if fm.enabled != 0 { - fm.m.RUnlock() - } -} - -func (fm *FlexMutex) Enable() { - fm.enabled++ -} - -func (fm *FlexMutex) Disable() { - if fm.enabled == 0 { - panic("FlexMutex is already disabled") - } - fm.enabled-- -} diff --git a/modules/engine/graph.go b/modules/engine/graph.go index 6238618..07aae41 100644 --- a/modules/engine/graph.go +++ b/modules/engine/graph.go @@ -11,9 +11,9 @@ type Graph struct { type GraphNode struct { *Object - Target bool - CanExpand int DynamicFields + CanExpand int + Target bool } func (n *GraphNode) Set(key string, value interface{}) { @@ -32,8 +32,8 @@ func (n *GraphNode) Get(key string) interface{} { type GraphEdge struct { Source, Target *Object - EdgeBitmap DynamicFields + EdgeBitmap } func (e *GraphEdge) Set(key string, value interface{}) { diff --git a/modules/engine/object.go b/modules/engine/object.go index 1167bb5..a200a3c 100644 --- a/modules/engine/object.go +++ b/modules/engine/object.go @@ -21,29 +21,13 @@ import ( "github.com/lkarlslund/stringdedup" ) -var adjustthreadsafe sync.Mutex -var threadsafeobject int var threadbuckets = runtime.NumCPU() * 64 - var threadsafeobjectmutexes = make([]sync.RWMutex, threadbuckets) func init() { stringdedup.YesIKnowThisCouldGoHorriblyWrong = true } -func setThreadsafe(enable bool) { - adjustthreadsafe.Lock() - if enable { - threadsafeobject++ - } else { - threadsafeobject-- - } - if threadsafeobject < 0 { - panic("threadsafeobject is negative") - } - adjustthreadsafe.Unlock() -} - var UnknownGUID = uuid.UUID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} type Object struct { @@ -57,26 +41,30 @@ type Object struct { id uint32 guid uuid.UUID // objectcategoryguid uuid.UUID - guidcached bool - sidcached bool - invalidated bool - objecttype ObjectType + guidcached bool + sidcached bool + isvalid bool + objecttype ObjectType } var IgnoreBlanks = "_IGNOREBLANKS_" func NewObject(flexinit ...interface{}) *Object { var result Object - result.init() + result.init(1) result.setFlex(flexinit...) return &result } +func NewPreload(preloadAttributes int) *Object { + var result Object + result.init(preloadAttributes) + + return &result +} + func (o *Object) ID() uint32 { - if o.id == 0 { - panic("no ID set on object, where did it come from?") - } return o.id } @@ -85,29 +73,41 @@ func (o *Object) lockbucket() int { } func (o *Object) lock() { - if threadsafeobject != 0 { - threadsafeobjectmutexes[o.lockbucket()].Lock() - } - if o.invalidated { + if !o.isvalid { panic("object is invalidated") } + threadsafeobjectmutexes[o.lockbucket()].Lock() } func (o *Object) rlock() { - if threadsafeobject != 0 { - threadsafeobjectmutexes[o.lockbucket()].RLock() - } + threadsafeobjectmutexes[o.lockbucket()].RLock() } func (o *Object) unlock() { - if threadsafeobject != 0 { - threadsafeobjectmutexes[o.lockbucket()].Unlock() - } + threadsafeobjectmutexes[o.lockbucket()].Unlock() } func (o *Object) runlock() { - if threadsafeobject != 0 { - threadsafeobjectmutexes[o.lockbucket()].RUnlock() + threadsafeobjectmutexes[o.lockbucket()].RUnlock() +} + +func (o *Object) lockwith(o2 *Object) { + ol := o.lockbucket() + o2l := o.lockbucket() + + threadsafeobjectmutexes[ol].Lock() + if ol != o2l { + threadsafeobjectmutexes[o2l].Lock() + } +} + +func (o *Object) unlockwith(o2 *Object) { + ol := o.lockbucket() + o2l := o.lockbucket() + + threadsafeobjectmutexes[ol].Unlock() + if ol != o2l { + threadsafeobjectmutexes[o2l].Unlock() } } @@ -146,21 +146,37 @@ func (o *Object) AbsorbEx(source *Object, fast bool) { }) } - for edgetarget, edges := range source.edges[Out] { - target.edges[Out][edgetarget] = target.edges[Out][edgetarget].Merge(edges) - delete(source.edges[Out], edgetarget) + source.edges[Out].Range(func(outgoingTarget *Object, edges EdgeBitmap) bool { + // Load edges from target, and merge with source edges + ot := (*Object)(outgoingTarget) + if te, loaded := target.edges[Out].GetOrSet(outgoingTarget, edges); loaded { + target.edges[Out].Set(outgoingTarget, te.Merge(edges)) + } + source.edges[Out].Del(outgoingTarget) + + if te, loaded := ot.edges[In].GetOrSet(target, edges); loaded { + ot.edges[In].Set(target, te.Merge(edges)) + } + ot.edges[In].Del(source) - edgetarget.edges[In][target] = edgetarget.edges[In][target].Merge(edges) - delete(edgetarget.edges[In], source) - } + return true + }) - for edgesource, edges := range source.edges[In] { - target.edges[In][edgesource] = target.edges[In][edgesource].Merge(edges) - delete(source.edges[In], edgesource) + source.edges[In].Range(func(incomingTarget *Object, edges EdgeBitmap) bool { + it := (*Object)(incomingTarget) - edgesource.edges[Out][target] = edgesource.edges[Out][target].Merge(edges) - delete(edgesource.edges[Out], source) - } + if se, loaded := target.edges[In].GetOrSet(incomingTarget, edges); loaded { + target.edges[In].Set(incomingTarget, se.Merge(edges)) + } + source.edges[In].Del(incomingTarget) + + if se, loaded := it.edges[Out].GetOrSet(target, edges); loaded { + it.edges[Out].Set(target, se.Merge(edges)) + } + it.edges[Out].Del(source) + + return true + }) for _, child := range source.children { target.adopt(child) @@ -180,17 +196,15 @@ func (o *Object) AbsorbEx(source *Object, fast bool) { // Both has a cache if !source.sdcache.Equals(target.sdcache) { // Different caches, so we need to merge them which is impossible - ui.Warn().Msgf("Can not merge security descriptors between %v and %v", source.Label(), target.Label()) + ui.Error().Msgf("Can not merge security descriptors between %v and %v", source.Label(), target.Label()) } } else if target.sdcache == nil && source.sdcache != nil { target.sdcache = source.sdcache - } else { - target.sdcache = nil } target.objecttype = 0 // Recalculate this - source.invalidated = true // Invalid object + source.isvalid = false // Invalid object } func MergeValues(v1, v2 AttributeValues) AttributeValues { @@ -211,26 +225,26 @@ func MergeValues(v1, v2 AttributeValues) AttributeValues { } } else { // One or more of them have more than one value, do it the hard way - var destinationSlice AttributeValueSlice - var testvalues AttributeValues + var biggest AttributeValues + var smallest AttributeValues if v1.Len() > v2.Len() { - destinationSlice = v1.Slice() - testvalues = v2 + biggest = v1 + smallest = v2 } else { - destinationSlice = v2.Slice() - testvalues = v1 + biggest = v2 + smallest = v1 } - resultingvalues := destinationSlice + resultingvalues := biggest.(AttributeValueSlice) - testvalues.Iterate(func(testvalue AttributeValue) bool { - for _, existingvalue := range destinationSlice { - if CompareAttributeValues(existingvalue, testvalue) { // Crap!! - return false + smallest.Iterate(func(valueFromSmallest AttributeValue) bool { + for _, existingvalue := range resultingvalues { + if CompareAttributeValues(existingvalue, valueFromSmallest) { // Crap!! + return true // Continue } } - resultingvalues = append(resultingvalues, testvalue) + resultingvalues = append(resultingvalues, valueFromSmallest) return true }) @@ -266,8 +280,6 @@ func (s StringMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error { } func (o *Object) NameStringMap() StringMap { - o.lock() - defer o.unlock() result := make(StringMap) o.values.Iterate(func(attr Attribute, values AttributeValues) bool { result[attr.String()] = values.StringSlice() @@ -381,7 +393,7 @@ func (o *Object) OneAttrRendered(attr Attribute) string { // Returns synthetic blank attribute value if it isn't set func (o *Object) get(attr Attribute) (AttributeValues, bool) { - if o.invalidated { + if !o.isvalid { panic("object is invalidated") } if attributenums[attr].onget != nil { @@ -392,8 +404,6 @@ func (o *Object) get(attr Attribute) (AttributeValues, bool) { // Auto locking version func (o *Object) Get(attr Attribute) (AttributeValues, bool) { - o.rlock() - defer o.runlock() return o.get(attr) } @@ -410,14 +420,10 @@ func (o *Object) attr(attr Attribute) AttributeValues { // Returns synthetic blank attribute value if it isn't set func (o *Object) Attr(attr Attribute) AttributeValues { - o.rlock() - defer o.runlock() return o.attr(attr) } func (o *Object) OneAttrString(attr Attribute) string { - o.rlock() - defer o.runlock() a, found := o.get(attr) if !found { return "" @@ -517,9 +523,7 @@ func (o *Object) SetValues(a Attribute, values ...AttributeValue) { } func (o *Object) SetFlex(flexinit ...interface{}) { - o.lock() o.setFlex(flexinit...) - o.unlock() } var avsPool sync.Pool @@ -678,15 +682,11 @@ func (o *Object) setFlex(flexinit ...interface{}) { } func (o *Object) Set(a Attribute, values AttributeValues) { - o.lock() o.set(a, values) - o.unlock() } func (o *Object) Clear(a Attribute) { - o.lock() o.values.Clear(a) - o.unlock() } func (o *Object) set(a Attribute, values AttributeValues) { @@ -793,12 +793,14 @@ func (o *Object) Meta() map[string]string { return result } -func (o *Object) init() { +func (o *Object) init(preloadAttributes int) { o.id = atomic.AddUint32(&idcounter, 1) - if o.edges[Out] == nil || o.edges[In] == nil { - o.edges[Out] = make(EdgeConnections) - o.edges[In] = make(EdgeConnections) + if preloadAttributes == 0 { + preloadAttributes = 1 } + o.values.init(preloadAttributes) + o.isvalid = true + onAddObject(o) } func (o *Object) String() string { @@ -809,19 +811,16 @@ func (o *Object) String() string { return true // continue } result += " " + attributenums[attr].name + ":\n" - for _, value := range values.Slice() { + values.Iterate(func(value AttributeValue) bool { cleanval := stringsx.Clean(value.String()) if cleanval != value.String() { result += fmt.Sprintf(" %v (%v original, %v cleaned)\n", value, len(value.String()), len(cleanval)) } else { result += " " + value.String() + "\n" } - } + return true + }) - if an, ok := values.(AttributeNode); ok { - // dump with recursion - fixme - an.Children() - } return true // one more }) return result @@ -890,8 +889,6 @@ func (o *Object) cacheSecurityDescriptor(rawsd []byte) error { // Return the object's SID func (o *Object) SID() windowssecurity.SID { if !o.sidcached { - o.lock() - o.sidcached = true if asid, ok := o.get(ObjectSid); ok { if asid.Len() == 1 { if sid, ok := asid.First().Raw().(windowssecurity.SID); ok { @@ -899,7 +896,7 @@ func (o *Object) SID() windowssecurity.SID { } } } - o.unlock() + o.sidcached = true } sid := o.sid return sid @@ -907,9 +904,7 @@ func (o *Object) SID() windowssecurity.SID { // Return the object's GUID func (o *Object) GUID() uuid.UUID { - o.lock() if !o.guidcached { - o.guidcached = true if aguid, ok := o.get(ObjectGUID); ok { if aguid.Len() == 1 { if guid, ok := aguid.First().Raw().(uuid.UUID); ok { @@ -917,19 +912,17 @@ func (o *Object) GUID() uuid.UUID { } } } + o.guidcached = true } guid := o.guid - o.unlock() return guid } // Look up edge -func (o *Object) Edge(direction EdgeDirection, target *Object) EdgeBitmap { - o.lock() - bm := o.edges[direction][target] - o.unlock() - return bm -} +// func (o *Object) Edge(direction EdgeDirection, target *Object) EdgeBitmap { +// bm, _ := o.edges[direction].Get(target) +// return bm +// } // Register that this object can pwn another object using the given method func (o *Object) EdgeTo(target *Object, method Edge) { @@ -947,7 +940,7 @@ func (o *Object) EdgeToEx(target *Object, method Edge, force bool) { osid := o.SID() // Ignore these, SELF = self own, Creator/Owner always has full rights - if osid == windowssecurity.SelfSID || osid == windowssecurity.SystemSID { + if osid == windowssecurity.SelfSID { return } @@ -957,71 +950,50 @@ func (o *Object) EdgeToEx(target *Object, method Edge, force bool) { } } - o.lock() - o.edges[Out].Set(target, method) // Add the connection - o.unlock() - - target.lock() - target.edges[In].Set(o, method) // Add the reverse connection too - target.unlock() + o.Edges(Out).SetEdge(target, method) // Add the connection + target.Edges(In).SetEdge(o, method) // Add the reverse connection too } -func (o *Object) EdgeClear(target *Object, method Edge) { - o.lock() - currentedge := o.edges[Out][target] - if currentedge.IsSet(method) { - o.edges[Out][target] = currentedge.Clear(method) - } - o.unlock() - - target.lock() - currentedge = target.edges[In][o] - if currentedge.IsSet(method) { - target.edges[In][o] = currentedge.Clear(method) - } - target.unlock() +type ObjectEdge struct { + o *Object + e EdgeBitmap } -func (o *Object) EdgeCount(direction EdgeDirection) int { - o.lock() - result := len(o.edges[direction]) - o.unlock() - return result +func (o *Object) Edges(direction EdgeDirection) *EdgeConnections { + return &o.edges[direction] } -type ObjectEdge struct { - o *Object - e EdgeBitmap +func (o *Object) EdgeIteratorRecursiveID(direction EdgeDirection, edgeMatch EdgeBitmap, af func(sourceid, targetid uint32, edge EdgeBitmap, depth int) bool) { + o.edgeIteratorRecursiveID(direction, edgeMatch, af, make(map[uint32]struct{}), 1) } -func (o *Object) EdgeIterator(direction EdgeDirection, ei func(target *Object, edge EdgeBitmap) bool) { - o.rlock() - edges := make([]ObjectEdge, len(o.edges[direction])) - var i int - for target, edge := range o.edges[direction] { - edges[i] = ObjectEdge{o: target, e: edge} - i++ - } - o.runlock() - for _, objectedge := range edges { - if !ei(objectedge.o, objectedge.e) { - return +func (o *Object) edgeIteratorRecursiveID(direction EdgeDirection, edgeMatch EdgeBitmap, af func(sourceid, targetid uint32, edge EdgeBitmap, depth int) bool, appliedTo map[uint32]struct{}, depth int) { + o.Edges(direction).RangeID(func(targetid uint32, edge EdgeBitmap) bool { + if _, found := appliedTo[targetid]; !found { + edgeMatches := edge.Intersect(edgeMatch) + if !edgeMatches.IsBlank() { + appliedTo[targetid] = struct{}{} + if af(o.ID(), targetid, edgeMatches, depth) { + IDtoOBject(targetid).edgeIteratorRecursiveID(direction, edgeMatch, af, appliedTo, depth+1) + } + } } - } + return true + }) } func (o *Object) EdgeIteratorRecursive(direction EdgeDirection, edgeMatch EdgeBitmap, af func(source, target *Object, edge EdgeBitmap, depth int) bool) { - o.applyToObjectEdgesRecursive(direction, edgeMatch, af, make(map[*Object]struct{}), 1) + o.edgeIteratorRecursive(direction, edgeMatch, af, make(map[*Object]struct{}), 1) } -func (o *Object) applyToObjectEdgesRecursive(direction EdgeDirection, edgeMatch EdgeBitmap, af func(source, target *Object, edge EdgeBitmap, depth int) bool, appliedTo map[*Object]struct{}, depth int) { - o.EdgeIterator(direction, func(target *Object, edge EdgeBitmap) bool { +func (o *Object) edgeIteratorRecursive(direction EdgeDirection, edgeMatch EdgeBitmap, af func(source, target *Object, edge EdgeBitmap, depth int) bool, appliedTo map[*Object]struct{}, depth int) { + o.Edges(direction).Range(func(target *Object, edge EdgeBitmap) bool { if _, found := appliedTo[target]; !found { edgeMatches := edge.Intersect(edgeMatch) if !edgeMatches.IsBlank() { appliedTo[target] = struct{}{} if af(o, target, edgeMatches, depth) { - target.applyToObjectEdgesRecursive(direction, edgeMatch, af, appliedTo, depth+1) + target.edgeIteratorRecursive(direction, edgeMatch, af, appliedTo, depth+1) } } } @@ -1034,15 +1006,13 @@ func (o *Object) AttrIterator(f func(attr Attribute, avs AttributeValues) bool) } func (o *Object) ChildOf(parent *Object) { - o.lock() if o.parent != nil { // Unlock, as we call thing that lock in the debug message - o.unlock() ui.Debug().Msgf("Object %v already has %v as parent, so I'm not assigning %v as parent", o.Label(), o.parent.Label(), parent.Label()) return - o.lock() // panic("objects can only have one parent") } + o.lock() o.parent = parent o.unlock() parent.lock() diff --git a/modules/engine/objectindex.go b/modules/engine/objectindex.go new file mode 100644 index 0000000..ef0f464 --- /dev/null +++ b/modules/engine/objectindex.go @@ -0,0 +1,50 @@ +package engine + +import ( + "runtime" + "sync" + "sync/atomic" + "unsafe" +) + +var RememberedObjects, NukedObjects uint64 + +// var idToObject gsync.MapOf[uint32, uintptr] + +var idToObject []uintptr +var idToObjectLen uint32 +var idToObjectLock sync.RWMutex + +func onDestroyObject(f *Object) { + idToObjectLock.Lock() + atomic.StoreUintptr(&idToObject[f.id], 0) + atomic.AddUint64(&NukedObjects, 1) + idToObjectLock.Unlock() +} + +func onAddObject(newObject *Object) { + idToObjectLock.Lock() + for idToObjectLen <= newObject.id { + newlen := newObject.id + 4096 + newObjects := make([]uintptr, newlen) + + copy(newObjects, idToObject) + idToObject = newObjects + + atomic.StoreUint32(&idToObjectLen, newlen) + } + idToObject[newObject.id] = uintptr(unsafe.Pointer(newObject)) + atomic.AddUint64(&RememberedObjects, 1) + runtime.SetFinalizer(newObject, onDestroyObject) + idToObjectLock.Unlock() +} + +func IDtoOBject(id uint32) *Object { + idToObjectLock.RLock() + objectPtr := atomic.LoadUintptr(&idToObject[id]) + if objectPtr == 0 { + panic("Asked to resolve an Object I forgot about") + } + idToObjectLock.RUnlock() + return (*Object)(unsafe.Pointer(objectPtr)) +} diff --git a/modules/engine/objects.go b/modules/engine/objects.go index 02b8a49..04e7cc1 100644 --- a/modules/engine/objects.go +++ b/modules/engine/objects.go @@ -1,8 +1,13 @@ package engine import ( + "runtime" "strings" + "sync" + "sync/atomic" + "time" + "github.com/akyoto/cache" "github.com/gofrs/uuid" "github.com/lkarlslund/adalanche/modules/ui" "github.com/lkarlslund/adalanche/modules/windowssecurity" @@ -16,12 +21,12 @@ type Objects struct { root *Object DefaultValues []interface{} - objectmutex FlexMutex + objectmutex sync.RWMutex asarray []*Object // All objects in this collection, returned by .Slice() asmap map[*Object]struct{} - indexlock FlexMutex + indexlock sync.RWMutex indexes []*Index multiindexes map[uint32]*Index // Indexes for multiple attributes (lower attr < 16 || higher attr) @@ -33,7 +38,7 @@ type Objects struct { type Index struct { lookup map[interface{}][]*Object - FlexMutex + sync.RWMutex } func (i *Index) Init() { @@ -87,29 +92,6 @@ func (os *Objects) AddDefaultFlex(data ...interface{}) { os.DefaultValues = append(os.DefaultValues, data...) } -func (os *Objects) SetThreadsafe(enable bool) { - if enable { - os.objectmutex.Enable() - os.indexlock.Enable() - - for _, index := range os.indexes { - if index != nil { - index.Enable() - } - } - } else { - os.objectmutex.Disable() - os.indexlock.Disable() - - for _, index := range os.indexes { - if index != nil { - index.Disable() - } - } - } - setThreadsafe(enable) // Do this globally for individial objects too -} - func (os *Objects) GetIndex(attribute Attribute) *Index { os.indexlock.RLock() @@ -143,11 +125,6 @@ func (os *Objects) GetIndex(attribute Attribute) *Index { // Initialize index and add existing stuff os.refreshIndex(attribute, index) - // Sync any locking stuff to the new index - for i := 0; i < int(os.objectmutex.enabled); i++ { - index.Enable() - } - os.indexes[attribute] = index } os.indexlock.Unlock() @@ -208,11 +185,6 @@ func (os *Objects) GetMultiIndex(attribute, attribute2 Attribute) *Index { // Initialize index and add existing stuff os.refreshMultiIndex(attribute, attribute2, index) - // Sync any locking stuff to the new index - for i := 0; i < int(os.objectmutex.enabled); i++ { - index.Enable() - } - os.multiindexes[indexkey] = index os.indexlock.Unlock() @@ -225,12 +197,14 @@ func (os *Objects) refreshIndex(attribute Attribute, index *Index) { // add all existing stuff to index for _, o := range os.asarray { - for _, value := range o.Attr(attribute).Slice() { + o.Attr(attribute).Iterate(func(value AttributeValue) bool { key := AttributeValueToIndex(value) // Add to index index.Add(key, o, false) - } + + return true // continue + }) } } @@ -247,17 +221,19 @@ func (os *Objects) refreshMultiIndex(attribute, attribute2 Attribute, index *Ind if !o.HasAttr(attribute) || !o.HasAttr(attribute2) { continue } - values := o.Attr(attribute).Slice() - values2 := o.Attr(attribute2).Slice() - for _, value := range values { + + o.Attr(attribute).Iterate(func(value AttributeValue) bool { key := AttributeValueToIndex(value) - for _, value2 := range values2 { + o.Attr(attribute2).Iterate(func(value2 AttributeValue) bool { key2 := AttributeValueToIndex(value2) // Add to index index.Add(multiindexkey{key, key2}, o, false) - } - } + + return true + }) + return true + }) } } @@ -320,23 +296,32 @@ func (os *Objects) ReindexObject(o *Object, isnew bool) { continue } - values := o.Attr(attribute).Slice() - values2 := o.Attr(attribute2).Slice() - for _, value := range values { + o.Attr(attribute).Iterate(func(value AttributeValue) bool { key := AttributeValueToIndex(value) - for _, value2 := range values2 { + o.Attr(attribute2).Iterate(func(value2 AttributeValue) bool { key2 := AttributeValueToIndex(value2) index.Add(multiindexkey{key, key2}, o, !isnew) - } - } + + return true + }) + return true + }) } os.indexlock.RUnlock() } +var avtiCache = cache.New(time.Second * 30) + func AttributeValueToIndex(value AttributeValue) interface{} { if vs, ok := value.(AttributeValueString); ok { - return strings.ToLower(string(vs)) + s := string(vs) + if lowered, found := avtiCache.Get(s); found { + return lowered + } + lowered := strings.ToLower(string(vs)) + avtiCache.Set(s, lowered, time.Second*30) + return lowered } return value.Raw() } @@ -508,6 +493,47 @@ func (os *Objects) Len() int { return len(os.asarray) } +func (os *Objects) Iterate(each func(o *Object) bool) { + for _, o := range os.asarray { + if !each(o) { + break + } + } +} + +func (os *Objects) IterateParallel(each func(o *Object) bool, parallelFuncs int) { + if parallelFuncs == 0 { + parallelFuncs = runtime.NumCPU() + } + queue := make(chan *Object, parallelFuncs*2) + var wg sync.WaitGroup + + var stop atomic.Bool + + for i := 0; i < parallelFuncs; i++ { + wg.Add(1) + go func() { + for o := range queue { + if !each(o) { + stop.Store(false) + } + } + wg.Done() + }() + } + + for i, o := range os.asarray { + if i&0x400 == 0 && stop.Load() { + ui.Debug().Msg("Aborting parallel iterator for Objects") + break + } + queue <- o + } + + close(queue) + wg.Wait() +} + func (os *Objects) FindByID(id uint32) (o *Object, found bool) { os.objectmutex.RLock() index, idfound := os.idindex[id] @@ -586,7 +612,7 @@ func (os *Objects) FindMultiOrAdd(attribute Attribute, value AttributeValue, add } func (os *Objects) FindTwoMultiOrAdd(attribute Attribute, value AttributeValue, attribute2 Attribute, value2 AttributeValue, addifnotfound func() *Object) ([]*Object, bool) { - if attribute < attribute2 { + if attribute2 != NonExistingAttribute && attribute < attribute2 { attribute, attribute2 = attribute2, attribute value, value2 = value2, value } diff --git a/modules/engine/processing.go b/modules/engine/processing.go index e367068..1692063 100644 --- a/modules/engine/processing.go +++ b/modules/engine/processing.go @@ -1,6 +1,8 @@ package engine import ( + "runtime" + "github.com/lkarlslund/adalanche/modules/ui" ) @@ -123,6 +125,10 @@ func Merge(aos []*Objects) (*Objects, error) { aftermergetotalobjects := len(globalobjects.Slice()) ui.Info().Msgf("After merge we have %v objects in the metaverse (merge eliminated %v objects)", aftermergetotalobjects, totalobjects-aftermergetotalobjects) + runtime.GC() + runtime.GC() + ui.Info().Msgf("We freed %v objects", NukedObjects) + var orphans int processed := make(map[uint32]struct{}) var processobject func(o *Object) diff --git a/modules/engine/pwnanalyzers.go b/modules/engine/pwnanalyzers.go index 2673d7b..9a6c567 100644 --- a/modules/engine/pwnanalyzers.go +++ b/modules/engine/pwnanalyzers.go @@ -52,7 +52,8 @@ func Process(ao *Objects, cb ProgressCallbackFunc, l LoaderID, priority ProcessP } } - total := len(priorityProcessors) * ao.Len() + aoLen := ao.Len() + total := len(priorityProcessors) * aoLen if total == 0 { return nil @@ -67,7 +68,7 @@ func Process(ao *Objects, cb ProgressCallbackFunc, l LoaderID, priority ProcessP wg.Add(1) go func(ppf ppfInfo) { ppf.pf(ao) - cb(-ao.Len(), 0) + cb(-aoLen, 0) wg.Done() }(processor) } diff --git a/modules/engine/run.go b/modules/engine/run.go index ca7db55..87aed74 100644 --- a/modules/engine/run.go +++ b/modules/engine/run.go @@ -52,8 +52,6 @@ func Run(path string) (*Objects, error) { preprocessWG.Add(1) go func(lobj loaderobjects) { - lobj.Objects.SetThreadsafe(true) - var loaderid LoaderID for i, loader := range loaders { if loader == lobj.Loader { @@ -78,8 +76,6 @@ func Run(path string) (*Objects, error) { pb.Finish() } - lobj.Objects.SetThreadsafe(false) - preprocessWG.Done() }(os) } @@ -92,8 +88,6 @@ func Run(path string) (*Objects, error) { } ao, err := Merge(objs) - ao.SetThreadsafe(true) - // Do global post-processing for priority := AfterMergeLow; priority <= AfterMergeFinal; priority++ { pb := ui.ProgressBar(fmt.Sprintf("Postprocessing merged objects priority %v", priority.String()), 0) @@ -110,8 +104,6 @@ func Run(path string) (*Objects, error) { pb.Finish() } - ao.SetThreadsafe(false) - var statarray []string for stat, count := range ao.Statistics() { if stat == 0 { diff --git a/modules/integrations/activedirectory/analyze/adloader.go b/modules/integrations/activedirectory/analyze/adloader.go index 82ebbc1..2e86bd4 100644 --- a/modules/integrations/activedirectory/analyze/adloader.go +++ b/modules/integrations/activedirectory/analyze/adloader.go @@ -129,8 +129,6 @@ func (ld *ADLoader) getShard(path string) *engine.Objects { shard := filepath.Dir(path) new_ao := engine.NewLoaderObjects(ld) - new_ao.SetThreadsafe(true) - ao, _ := ld.shardobjects.LoadOrStore(shard, new_ao) return ao } @@ -203,8 +201,6 @@ func (ld *ADLoader) Close() ([]*engine.Objects, error) { } aos = append(aos, ao) - ao.SetThreadsafe(false) - return true // next }) diff --git a/modules/integrations/activedirectory/analyze/analyze-ad.go b/modules/integrations/activedirectory/analyze/analyze-ad.go index 452de8d..c875de2 100644 --- a/modules/integrations/activedirectory/analyze/analyze-ad.go +++ b/modules/integrations/activedirectory/analyze/analyze-ad.go @@ -591,12 +591,12 @@ func init() { }, "Change user script path (allows an attacker to trigger a user auth against an attacker controlled UNC path)", engine.BeforeMergeFinal) Loader.AddProcessor(func(ao *engine.Objects) { for _, o := range ao.Slice() { - msas := o.Attr(activedirectory.MSDSHostServiceAccount).Slice() - for _, dn := range msas { + o.Attr(activedirectory.MSDSHostServiceAccount).Iterate(func(dn engine.AttributeValue) bool { if targetmsa, found := ao.Find(engine.DistinguishedName, dn); found { o.EdgeTo(targetmsa, activedirectory.EdgeHasMSA) } - } + return true + }) } }, "Indicates that the object has a service account in use", engine.BeforeMergeFinal) @@ -621,13 +621,13 @@ func init() { Loader.AddProcessor(func(ao *engine.Objects) { for _, o := range ao.Slice() { - sids := o.Attr(activedirectory.SIDHistory).Slice() - for _, sidval := range sids { + o.Attr(activedirectory.SIDHistory).Iterate(func(sidval engine.AttributeValue) bool { if sid, ok := sidval.Raw().(windowssecurity.SID); ok { target := ao.FindOrAddAdjacentSID(sid, o) o.EdgeTo(target, activedirectory.EdgeSIDHistoryEquality) } - } + return true + }) } }, "Indicates that object has a SID History attribute pointing to the other object, making them the 'same' permission wise", engine.BeforeMergeFinal) @@ -747,7 +747,7 @@ func init() { } // Add the DCsync combination flag - o.EdgeIterator(engine.In, func(target *engine.Object, edge engine.EdgeBitmap) bool { + o.Edges(engine.In).Range(func(target *engine.Object, edge engine.EdgeBitmap) bool { if edge.IsSet(activedirectory.EdgeDSReplicationGetChanges) && edge.IsSet(activedirectory.EdgeDSReplicationGetChangesAll) { // DCsync attack WOT WOT target.EdgeTo(o, activedirectory.EdgeDCsync) @@ -1315,7 +1315,7 @@ func init() { // Find the computer AD object if any var computer *engine.Object - machine.EdgeIterator(engine.Out, func(target *engine.Object, edge engine.EdgeBitmap) bool { + machine.Edges(engine.Out).Range(func(target *engine.Object, edge engine.EdgeBitmap) bool { if edge.IsSet(EdgeAuthenticatesAs) && target.Type() == engine.ObjectTypeComputer { computer = target return false //break @@ -1391,22 +1391,22 @@ func init() { } // cached or generated - pairwise pointer to gpo object and int - gplinkslice := gpcachelinks.Slice() - for i := 0; i < gpcachelinks.Len(); i += 2 { - gpo := gplinkslice[i].Raw().(*engine.Object) - gpLinkOptions := gplinkslice[i+1].Raw().(int64) - // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-gpol/08090b22-bc16-49f4-8e10-f27a8fb16d18 - if gpLinkOptions&0x01 != 0 { - // GPO link is disabled - continue - } - if allowEnforcedGPOsOnly && gpLinkOptions&0x02 == 0 { - // Enforcement required, but this is not an enforced GPO - continue + if gplinkslice, ok := gpcachelinks.(engine.AttributeValueSlice); ok { + for i := 0; i < gpcachelinks.Len(); i += 2 { + gpo := gplinkslice[i].Raw().(*engine.Object) + gpLinkOptions := gplinkslice[i+1].Raw().(int64) + // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-gpol/08090b22-bc16-49f4-8e10-f27a8fb16d18 + if gpLinkOptions&0x01 != 0 { + // GPO link is disabled + continue + } + if allowEnforcedGPOsOnly && gpLinkOptions&0x02 == 0 { + // Enforcement required, but this is not an enforced GPO + continue + } + gpo.EdgeTo(machine, activedirectory.EdgeAffectedByGPO) } - gpo.EdgeTo(machine, activedirectory.EdgeAffectedByGPO) } - gpoptions := p.OneAttrString(activedirectory.GPOptions) if gpoptions == "1" { // inheritance is blocked, so let's not forget that when moving up @@ -1536,10 +1536,10 @@ func init() { ) Loader.AddProcessor(func(ao *engine.Objects) { - for _, o := range ao.Slice() { + ao.IterateParallel(func(o *engine.Object) bool { // Object that is member of something if o.Type() != engine.ObjectTypeGroup { - continue + return true } o.EdgeIteratorRecursive(engine.In, engine.EdgeBitmap{}.Set(activedirectory.EdgeMemberOfGroup).Set(activedirectory.EdgeForeignIdentity), func(source, member *engine.Object, edge engine.EdgeBitmap, depth int) bool { @@ -1548,7 +1548,8 @@ func init() { } return true }) - } + return true + }, 0) }, "MemberOfIndirect resolution", engine.AfterMerge, @@ -1584,14 +1585,14 @@ func init() { for _, gpo := range ao.Filter(func(o *engine.Object) bool { return o.Type() == engine.ObjectTypeGroupPolicyContainer }).Slice() { - gpo.EdgeIterator(engine.In, func(group *engine.Object, methods engine.EdgeBitmap) bool { + gpo.Edges(engine.In).Range(func(group *engine.Object, methods engine.EdgeBitmap) bool { groupname := group.OneAttrString(engine.SAMAccountName) if strings.Contains(groupname, "%") { // Lowercase for ease groupname := strings.ToLower(groupname) // It has some sort of % variable in it, let's go - gpo.EdgeIterator(engine.Out, func(affected *engine.Object, amethods engine.EdgeBitmap) bool { + gpo.Edges(engine.Out).Range(func(affected *engine.Object, amethods engine.EdgeBitmap) bool { if amethods.IsSet(activedirectory.EdgeAffectedByGPO) && affected.Type() == engine.ObjectTypeComputer { netbiosdomain, computername, found := strings.Cut(affected.OneAttrString(engine.DownLevelLogonName), "\\") if !found { diff --git a/modules/integrations/activedirectory/analyze/gpoloader.go b/modules/integrations/activedirectory/analyze/gpoloader.go index f29d7d4..7e73832 100644 --- a/modules/integrations/activedirectory/analyze/gpoloader.go +++ b/modules/integrations/activedirectory/analyze/gpoloader.go @@ -105,7 +105,6 @@ func (ld *GPOLoader) getShard(path string) *engine.Objects { if ao == nil { ao = engine.NewLoaderObjects(ld) - ao.SetThreadsafe(true) ld.dco[lookupshard] = ao } ld.importmutex.Unlock() @@ -127,7 +126,6 @@ func (ld *GPOLoader) Close() ([]*engine.Objects, error) { var aos []*engine.Objects for _, ao := range ld.dco { aos = append(aos, ao) - ao.SetThreadsafe(false) } ld.dco = nil diff --git a/modules/integrations/activedirectory/rawobject.go b/modules/integrations/activedirectory/rawobject.go index 03b836c..fa8b54f 100644 --- a/modules/integrations/activedirectory/rawobject.go +++ b/modules/integrations/activedirectory/rawobject.go @@ -27,9 +27,12 @@ func (r *RawObject) Init() { } func (r *RawObject) ToObject(onlyKnownAttributes bool) *engine.Object { - result := engine.NewObject( + result := engine.NewPreload(len(r.Attributes)) + + result.SetFlex( DistinguishedName, engine.AttributeValueString(r.DistinguishedName), ) // This is possibly repeated in member attributes, so dedup it + for name, values := range r.Attributes { if len(values) == 0 || (len(values) == 1 && values[0] == "") { continue diff --git a/modules/integrations/localmachine/analyze/analyzer.go b/modules/integrations/localmachine/analyze/analyzer.go index 50454cb..17ff29d 100644 --- a/modules/integrations/localmachine/analyze/analyzer.go +++ b/modules/integrations/localmachine/analyze/analyzer.go @@ -16,7 +16,7 @@ var ( EdgeLocalAdminRights = engine.NewEdge("AdminRights") EdgeLocalRDPRights = engine.NewEdge("RDPRights").RegisterProbabilityCalculator(func(source, target *engine.Object) engine.Probability { var probability engine.Probability - target.EdgeIterator(engine.In, func(potential *engine.Object, edge engine.EdgeBitmap) bool { + target.Edges(engine.In).Range(func(potential *engine.Object, edge engine.EdgeBitmap) bool { sid := potential.SID() if sid.IsBlank() { return true // continue diff --git a/modules/integrations/localmachine/analyze/loader.go b/modules/integrations/localmachine/analyze/loader.go index 7042d6f..27f08d1 100644 --- a/modules/integrations/localmachine/analyze/loader.go +++ b/modules/integrations/localmachine/analyze/loader.go @@ -37,7 +37,6 @@ func (ld *LocalMachineLoader) Name() string { func (ld *LocalMachineLoader) Init() error { ld.ao = engine.NewLoaderObjects(ld) - ld.ao.SetThreadsafe(true) ld.machinesids = make(map[string][]*engine.Object) ld.infostoadd = make(chan loaderQueueItem, 128) @@ -84,7 +83,6 @@ func (ld *LocalMachineLoader) Init() error { func (ld *LocalMachineLoader) Close() ([]*engine.Objects, error) { close(ld.infostoadd) ld.done.Wait() - ld.ao.SetThreadsafe(false) // Knot all the objects with colliding SIDs together for _, os := range ld.machinesids { diff --git a/modules/integrations/localmachine/analyze/postprocessing.go b/modules/integrations/localmachine/analyze/postprocessing.go index 3dd026c..d011a82 100644 --- a/modules/integrations/localmachine/analyze/postprocessing.go +++ b/modules/integrations/localmachine/analyze/postprocessing.go @@ -51,7 +51,7 @@ func init() { for _, o := range ao.Slice() { if o.HasAttrValue(engine.DataLoader, ln) { if o.HasAttr(activedirectory.ObjectSid) { - if o.EdgeCount(engine.Out)+o.EdgeCount(engine.In) == 0 { + if o.Edges(engine.Out).Len()+o.Edges(engine.In).Len() == 0 { ui.Debug().Msgf("Object has no graph connections: %v", o.Label()) } warns++ diff --git a/modules/query/types.go b/modules/query/types.go index dbbeb3b..ca1a89e 100644 --- a/modules/query/types.go +++ b/modules/query/types.go @@ -351,24 +351,34 @@ func (td timediffModifier) Evaluate(a engine.Attribute, o *engine.Object) bool { } var result bool - val2slice := val2s.Slice() var i int val1s.Iterate(func(value1 engine.AttributeValue) bool { - i++ t1, ok := value1.Raw().(time.Time) if !ok { - return true + return true // break } - t2, ok2 := val2slice[i-1].Raw().(time.Time) - if !ok2 { - return false + var t2 time.Time + var t2ok bool + // Jump to the right entry to evaluate + var j int + val2s.Iterate(func(maybeVal2 engine.AttributeValue) bool { + if i == j { + t2, t2ok = maybeVal2.Raw().(time.Time) + return false + } + j++ + return true + }) + if !t2ok { + return false // break } if td.c.Compare(t1.Unix(), td.ts.From(t2).Unix()) { result = true - return false + return false // break } + i++ return true // next }) return result @@ -649,9 +659,9 @@ type pwnquery struct { func (p pwnquery) Evaluate(o *engine.Object) bool { var result bool - o.EdgeIterator(p.direction, func(target *engine.Object, edge engine.EdgeBitmap) bool { + o.Edges(p.direction).RangeID(func(targetid uint32, edge engine.EdgeBitmap) bool { if (p.method == engine.AnyEdgeType && !edge.IsBlank()) || edge.IsSet(p.method) { - if p.target == nil || p.target.Evaluate(target) { + if p.target == nil || p.target.Evaluate(engine.IDtoOBject(targetid)) { result = true return false // return from loop }