Skip to content

Commit

Permalink
Implemented Hunts label conditions.
Browse files Browse the repository at this point in the history
  • Loading branch information
scudette committed Jan 13, 2019
1 parent c8d18ed commit 18065bf
Show file tree
Hide file tree
Showing 15 changed files with 133 additions and 40 deletions.
51 changes: 30 additions & 21 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions api/artifacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ func setArtifactFile(config_obj *api_proto.Config,
}
defer fd.Close()

// We want to completely replace the content of the file.
fd.Truncate(0)

_, err = fd.Write([]byte(artifact))
if err != nil {
return err
Expand Down
5 changes: 4 additions & 1 deletion api/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,11 @@ func LabelClients(
}

index_func := db.SetIndex
if in.Operation == "remove" {
switch in.Operation {
case "remove":
index_func = db.UnsetIndex
case "check":
index_func = db.CheckIndex
}

for _, label := range in.Labels {
Expand Down
8 changes: 8 additions & 0 deletions artifacts/testdata/server/testcases/labels.in.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ Queries:
# Should be able to see our labels now.
- SELECT * FROM clients(search='label:*') ORDER BY client_id

# Check for labels
- SELECT label(client_id='C.11a3013cca8f826e',
labels=['WinBoxes'], op='check') AS HasLabel FROM scope()

# Should return Null - no such label
- SELECT label(client_id='C.11a3013cca8f826e',
labels=['NoWinBoxes'], op='check') AS HasLabel FROM scope()

# Clear everything
- |
SELECT label(client_id=client_id, op='remove', labels=labels), client_id
Expand Down
14 changes: 14 additions & 0 deletions artifacts/testdata/server/testcases/labels.out.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ FROM clients(search='label:*') ORDER BY client_id
"WinBoxes"
]
}
]SELECT label(client_id='C.11a3013cca8f826e', labels=['WinBoxes'], op='check') AS HasLabel FROM scope()[
{
"HasLabel": {
"ClientId": "C.11a3013cca8f826e",
"Labels": [
"WinBoxes"
],
"Op": "check"
}
}
]SELECT label(client_id='C.11a3013cca8f826e', labels=['NoWinBoxes'], op='check') AS HasLabel FROM scope()[
{
"HasLabel": null
}
]SELECT label(client_id=client_id, op='remove', labels=labels), client_id
FROM clients(search='label:*') ORDER BY client_id
[
Expand Down
6 changes: 6 additions & 0 deletions datastore/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ type DataStore interface {
entity string,
keywords []string) error

CheckIndex(
config_obj *api_proto.Config,
index_urn string,
entity string,
keywords []string) error

SearchClients(
config_obj *api_proto.Config,
index_urn string,
Expand Down
27 changes: 24 additions & 3 deletions datastore/filebased.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ func (self *FileBaseDataStore) GetSubject(
urn string,
message proto.Message) error {

serialized_content, err := readContentFromFile(config_obj, urn)
serialized_content, err := readContentFromFile(
config_obj, urn, false /* must_exist */)
if err != nil {
return err
}
Expand Down Expand Up @@ -281,6 +282,24 @@ func (self *FileBaseDataStore) UnsetIndex(
return nil
}

func (self *FileBaseDataStore) CheckIndex(
config_obj *api_proto.Config,
index_urn string,
entity string,
keywords []string) error {
for _, keyword := range keywords {
subject := path.Join(index_urn, strings.ToLower(keyword), entity)
_, err := readContentFromFile(
config_obj, subject, true /* must_exist */)
// Any of the matching entities means we checked
// successfully.
if err == nil {
return nil
}
}
return errors.New("Client does not have label")
}

func (self *FileBaseDataStore) SearchClients(
config_obj *api_proto.Config,
index_urn string,
Expand Down Expand Up @@ -498,15 +517,17 @@ func writeContentToFile(config_obj *api_proto.Config, urn string, data []byte) e
return nil
}

func readContentFromFile(config_obj *api_proto.Config, urn string) ([]byte, error) {
func readContentFromFile(
config_obj *api_proto.Config, urn string,
must_exist bool) ([]byte, error) {
filename, err := urnToFilename(config_obj, urn)
if err != nil {
return nil, err
}

file, err := os.Open(filename)
if err != nil {
if os.IsNotExist(err) {
if !must_exist && os.IsNotExist(err) {
return []byte{}, nil
}
return nil, errors.WithStack(err)
Expand Down
1 change: 1 addition & 0 deletions file_store/file_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
type WriteSeekCloser interface {
io.WriteSeeker
io.Closer
Truncate(size int64) error
}

type ReadSeekCloser interface {
Expand Down
4 changes: 4 additions & 0 deletions flows/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ func GetFlows(
func GetFlowDetails(
config_obj *api_proto.Config,
client_id string, flow_id string) (*api_proto.ApiFlow, error) {
if flow_id == "" || client_id == "" {
return &api_proto.ApiFlow{}, nil
}

flow_urn, err := ValidateFlowId(client_id, flow_id)
if err != nil {
return nil, err
Expand Down
7 changes: 0 additions & 7 deletions flows/foreman.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,6 @@ func (self *Foreman) ProcessMessage(
return nil
}

// TODO Check for labels.
/*
label_condition := hunt.Condition.GetLabels()
if label_condition != nil && len(label_condition.Label) > 0 {
}
*/

flow_condition_query, err := calculateFlowConditionQuery(hunt)
if err != nil {
return err
Expand Down
3 changes: 0 additions & 3 deletions flows/hunts.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"www.velocidex.com/golang/velociraptor/grpc_client"
"www.velocidex.com/golang/velociraptor/services"
urns "www.velocidex.com/golang/velociraptor/urns"
"www.velocidex.com/golang/velociraptor/utils"
)

func GetNewHuntId() string {
Expand Down Expand Up @@ -175,8 +174,6 @@ func GetHunt(config_obj *api_proto.Config, in *api_proto.GetHuntRequest) (
// stopped hunts. It is not possible to go back and re-examine the
// queue.
func ModifyHunt(config_obj *api_proto.Config, hunt_modification *api_proto.Hunt) error {
utils.Debug(hunt_modification)

dispatcher := services.GetHuntDispatcher()
err := dispatcher.ModifyHunt(
path.Base(hunt_modification.HuntId),
Expand Down
4 changes: 3 additions & 1 deletion flows/interrogate.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ func (self *VInterrogate) Start(
// Run standard queries.
queries := []*actions_proto.VQLRequest{
&actions_proto.VQLRequest{
VQL: "select Version.Name, Version.BuildTime, Client.Labels from config",
VQL: "select Version.Name AS Name, " +
"Version.BuildTime as BuildTime, " +
"Client.Labels as Labels from config",
Name: "Client Info"},
&actions_proto.VQLRequest{
VQL: "select Hostname, OS, Architecture, Platform, PlatformVersion, " +
Expand Down
2 changes: 1 addition & 1 deletion gui/static/angular-components/client/host-info.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ <h1>
<button ng-if="!readOnly"
class="btn btn-default"
ng-click="controller.interrogate()"
ng-disabled="!controller.hasClientAccess || controller.interrogateOperationId">
ng-disabled="controller.interrogateOperationId">
<i class="fa fa-search-plus"
ng-if="!controller.interrogateOperationId"></i>
<i class="fa fa-spinner fa-spin fa-fw"
Expand Down
35 changes: 35 additions & 0 deletions services/hunt_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@ func (self *HuntManager) ProcessRow(
return errors.New("hunt is stopped")
}

// Ignore hunts with label conditions which
// exclude this client.
if !huntHasLabel(
self.config_obj,
hunt_obj,
participation_row.ClientId) {
return errors.New("hunt label does not match")
}

// Hunt limit exceeded or it expired - we stop it.
if (hunt_obj.ClientLimit > 0 &&
hunt_obj.Stats.TotalClientsScheduled >= hunt_obj.ClientLimit) ||
Expand Down Expand Up @@ -196,3 +205,29 @@ func startHuntManager(config_obj *api_proto.Config) (
err := result.Start()
return result, err
}

func huntHasLabel(config_obj *api_proto.Config,
hunt_obj *api_proto.Hunt,
client_id string) bool {

label_condition := hunt_obj.Condition.GetLabels()
if label_condition != nil && len(label_condition.Label) > 0 {
channel := grpc_client.GetChannel(config_obj)
defer channel.Close()

client := api_proto.NewAPIClient(channel)
request := &api_proto.LabelClientsRequest{
ClientIds: []string{client_id},
Labels: label_condition.Label,
Operation: "check",
}
_, err := client.LabelClients(
context.Background(), request)

if err != nil {
return false
}
}

return true
}
Loading

0 comments on commit 18065bf

Please sign in to comment.