diff --git a/api/api.go b/api/api.go index c50398a3d9d..f1965df49f4 100644 --- a/api/api.go +++ b/api/api.go @@ -24,6 +24,7 @@ import ( "fmt" "net" "net/http" + "strings" "github.com/golang/protobuf/ptypes/empty" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -38,7 +39,6 @@ import ( "google.golang.org/grpc/status" actions_proto "www.velocidex.com/golang/velociraptor/actions/proto" api_proto "www.velocidex.com/golang/velociraptor/api/proto" - artifacts "www.velocidex.com/golang/velociraptor/artifacts" artifacts_proto "www.velocidex.com/golang/velociraptor/artifacts/proto" "www.velocidex.com/golang/velociraptor/constants" "www.velocidex.com/golang/velociraptor/datastore" @@ -103,10 +103,12 @@ func (self *ApiServer) LaunchFlow( ctx context.Context, in *flows_proto.FlowRunnerArgs) (*api_proto.StartFlowResponse, error) { result := &api_proto.StartFlowResponse{} - in.Creator = GetGRPCUserInfo(ctx).Name + creator := GetGRPCUserInfo(ctx).Name + + // Internal calls from the frontend can set the creator. + if creator != constants.FRONTEND_NAME { + in.Creator = creator - // Empty creators are called internally. - if in.Creator != "" { // If user is not found then reject it. user_record, err := users.GetUser(self.config, in.Creator) if err != nil { @@ -491,19 +493,10 @@ func (self *ApiServer) GetArtifacts( ctx context.Context, in *api_proto.GetArtifactsRequest) ( *artifacts_proto.ArtifactDescriptors, error) { - result := &artifacts_proto.ArtifactDescriptors{} - - repository, err := artifacts.GetGlobalRepository(self.config) - if err != nil { - return nil, err - } - for _, name := range repository.List() { - artifact, pres := repository.Get(name) - if pres { - result.Items = append(result.Items, artifact) - } - } - return result, nil + terms := strings.Split(in.SearchTerm, " ") + result, err := searchArtifact( + self.config, terms, in.Type, in.NumberOfResults) + return result, err } func (self *ApiServer) GetArtifactFile( diff --git a/api/artifacts.go b/api/artifacts.go index 2eea9718785..290cde291ea 100644 --- a/api/artifacts.go +++ b/api/artifacts.go @@ -21,6 +21,7 @@ import ( "encoding/json" "errors" "path" + "regexp" "strings" actions_proto "www.velocidex.com/golang/velociraptor/actions/proto" @@ -195,3 +196,62 @@ func renderBuiltinArtifacts( }, }, nil } + +func searchArtifact( + config_obj *api_proto.Config, + terms []string, + artifact_type string, + number_of_results uint64) ( + *artifacts_proto.ArtifactDescriptors, error) { + + if number_of_results == 0 { + number_of_results = 100 + } + + result := &artifacts_proto.ArtifactDescriptors{} + regexes := []*regexp.Regexp{} + for _, term := range terms { + if len(term) <= 2 { + continue + } + + re, err := regexp.Compile("(?i)" + term) + if err == nil { + regexes = append(regexes, re) + } + } + + if len(regexes) == 0 { + return result, nil + } + + matcher := func(text string, regexes []*regexp.Regexp) bool { + for _, re := range regexes { + if re.FindString(text) == "" { + return false + } + } + return true + } + + repository, err := artifacts.GetGlobalRepository(config_obj) + if err != nil { + return nil, err + } + + for _, name := range repository.List() { + artifact, pres := repository.Get(name) + if pres { + if matcher(artifact.Description, regexes) || + matcher(artifact.Name, regexes) { + result.Items = append(result.Items, artifact) + } + } + + if len(result.Items) >= int(number_of_results) { + break + } + } + + return result, nil +} diff --git a/api/auth.go b/api/auth.go index a889a682159..cf93f56ba15 100644 --- a/api/auth.go +++ b/api/auth.go @@ -22,8 +22,11 @@ import ( "encoding/json" "net/http" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" + "google.golang.org/grpc/peer" api_proto "www.velocidex.com/golang/velociraptor/api/proto" + "www.velocidex.com/golang/velociraptor/constants" "www.velocidex.com/golang/velociraptor/logging" users "www.velocidex.com/golang/velociraptor/users" ) @@ -107,12 +110,26 @@ func getClientApprovalForUser( func GetGRPCUserInfo(ctx context.Context) *api_proto.VelociraptorUser { result := &api_proto.VelociraptorUser{} - md, ok := metadata.FromIncomingContext(ctx) + peer, ok := peer.FromContext(ctx) if ok { - userinfo := md.Get("USER") - if len(userinfo) > 0 { - data := []byte(userinfo[0]) - json.Unmarshal(data, result) + tlsInfo, ok := peer.AuthInfo.(credentials.TLSInfo) + if ok { + v := tlsInfo.State.PeerCertificates[0].Subject.CommonName + // Calls from the gRPC gateway embed the + // authenticated web user in the metadata. + if v == constants.GRPC_GW_CLIENT_NAME { + md, ok := metadata.FromIncomingContext(ctx) + if ok { + userinfo := md.Get("USER") + if len(userinfo) > 0 { + data := []byte(userinfo[0]) + json.Unmarshal(data, result) + } + } + + } else { + result.Name = v + } } } diff --git a/api/proto/artifacts.pb.go b/api/proto/artifacts.pb.go index dabd0de5499..00dfb6f9a89 100644 --- a/api/proto/artifacts.pb.go +++ b/api/proto/artifacts.pb.go @@ -21,8 +21,12 @@ var _ = math.Inf const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package type GetArtifactsRequest struct { + // Deprecated IncludeEventArtifacts bool `protobuf:"varint,1,opt,name=include_event_artifacts,json=includeEventArtifacts,proto3" json:"include_event_artifacts,omitempty"` IncludeServerArtifacts bool `protobuf:"varint,2,opt,name=include_server_artifacts,json=includeServerArtifacts,proto3" json:"include_server_artifacts,omitempty"` + SearchTerm string `protobuf:"bytes,3,opt,name=search_term,json=searchTerm,proto3" json:"search_term,omitempty"` + NumberOfResults uint64 `protobuf:"varint,4,opt,name=number_of_results,json=numberOfResults,proto3" json:"number_of_results,omitempty"` + Type string `protobuf:"bytes,5,opt,name=type,proto3" json:"type,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -32,7 +36,7 @@ func (m *GetArtifactsRequest) Reset() { *m = GetArtifactsRequest{} } func (m *GetArtifactsRequest) String() string { return proto.CompactTextString(m) } func (*GetArtifactsRequest) ProtoMessage() {} func (*GetArtifactsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_artifacts_cfe4094041344ee4, []int{0} + return fileDescriptor_artifacts_a25917c1f4a78cbd, []int{0} } func (m *GetArtifactsRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GetArtifactsRequest.Unmarshal(m, b) @@ -66,7 +70,29 @@ func (m *GetArtifactsRequest) GetIncludeServerArtifacts() bool { return false } +func (m *GetArtifactsRequest) GetSearchTerm() string { + if m != nil { + return m.SearchTerm + } + return "" +} + +func (m *GetArtifactsRequest) GetNumberOfResults() uint64 { + if m != nil { + return m.NumberOfResults + } + return 0 +} + +func (m *GetArtifactsRequest) GetType() string { + if m != nil { + return m.Type + } + return "" +} + type GetArtifactRequest struct { + // Deprecated. VfsPath string `protobuf:"bytes,1,opt,name=vfs_path,json=vfsPath,proto3" json:"vfs_path,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -77,7 +103,7 @@ func (m *GetArtifactRequest) Reset() { *m = GetArtifactRequest{} } func (m *GetArtifactRequest) String() string { return proto.CompactTextString(m) } func (*GetArtifactRequest) ProtoMessage() {} func (*GetArtifactRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_artifacts_cfe4094041344ee4, []int{1} + return fileDescriptor_artifacts_a25917c1f4a78cbd, []int{1} } func (m *GetArtifactRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GetArtifactRequest.Unmarshal(m, b) @@ -115,7 +141,7 @@ func (m *GetArtifactResponse) Reset() { *m = GetArtifactResponse{} } func (m *GetArtifactResponse) String() string { return proto.CompactTextString(m) } func (*GetArtifactResponse) ProtoMessage() {} func (*GetArtifactResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_artifacts_cfe4094041344ee4, []int{2} + return fileDescriptor_artifacts_a25917c1f4a78cbd, []int{2} } func (m *GetArtifactResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GetArtifactResponse.Unmarshal(m, b) @@ -154,7 +180,7 @@ func (m *SetArtifactRequest) Reset() { *m = SetArtifactRequest{} } func (m *SetArtifactRequest) String() string { return proto.CompactTextString(m) } func (*SetArtifactRequest) ProtoMessage() {} func (*SetArtifactRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_artifacts_cfe4094041344ee4, []int{3} + return fileDescriptor_artifacts_a25917c1f4a78cbd, []int{3} } func (m *SetArtifactRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SetArtifactRequest.Unmarshal(m, b) @@ -200,7 +226,7 @@ func (m *APIResponse) Reset() { *m = APIResponse{} } func (m *APIResponse) String() string { return proto.CompactTextString(m) } func (*APIResponse) ProtoMessage() {} func (*APIResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_artifacts_cfe4094041344ee4, []int{4} + return fileDescriptor_artifacts_a25917c1f4a78cbd, []int{4} } func (m *APIResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_APIResponse.Unmarshal(m, b) @@ -254,7 +280,7 @@ func (m *GetReportRequest) Reset() { *m = GetReportRequest{} } func (m *GetReportRequest) String() string { return proto.CompactTextString(m) } func (*GetReportRequest) ProtoMessage() {} func (*GetReportRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_artifacts_cfe4094041344ee4, []int{5} + return fileDescriptor_artifacts_a25917c1f4a78cbd, []int{5} } func (m *GetReportRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GetReportRequest.Unmarshal(m, b) @@ -340,7 +366,7 @@ func (m *GetReportResponse) Reset() { *m = GetReportResponse{} } func (m *GetReportResponse) String() string { return proto.CompactTextString(m) } func (*GetReportResponse) ProtoMessage() {} func (*GetReportResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_artifacts_cfe4094041344ee4, []int{6} + return fileDescriptor_artifacts_a25917c1f4a78cbd, []int{6} } func (m *GetReportResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GetReportResponse.Unmarshal(m, b) @@ -391,51 +417,54 @@ func init() { proto.RegisterType((*GetReportResponse)(nil), "proto.GetReportResponse") } -func init() { proto.RegisterFile("artifacts.proto", fileDescriptor_artifacts_cfe4094041344ee4) } - -var fileDescriptor_artifacts_cfe4094041344ee4 = []byte{ - // 679 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x53, 0xcd, 0x6e, 0xd3, 0x4a, - 0x14, 0x56, 0x9a, 0x36, 0x49, 0xa7, 0xb7, 0xba, 0xbd, 0x73, 0x75, 0x6f, 0x4d, 0x60, 0x71, 0x64, - 0x24, 0x14, 0x24, 0xe4, 0xf0, 0x23, 0x41, 0x55, 0x01, 0x22, 0x51, 0x4b, 0x15, 0xd1, 0x3f, 0xdc, - 0x0a, 0x09, 0x75, 0x11, 0x0d, 0xf6, 0x71, 0x62, 0xc9, 0x9e, 0x71, 0x66, 0x26, 0x76, 0xbb, 0x66, - 0xc1, 0x43, 0xf0, 0x20, 0xbc, 0x00, 0x4f, 0x02, 0xaf, 0xc1, 0x02, 0x79, 0xc6, 0x31, 0xe9, 0xae, - 0x62, 0xc1, 0x2a, 0x93, 0xf3, 0x9d, 0xef, 0x3b, 0xdf, 0xf9, 0x31, 0xf9, 0x9b, 0x49, 0x1d, 0x47, - 0x2c, 0xd0, 0xca, 0xcb, 0xa4, 0xd0, 0x82, 0xae, 0x99, 0x9f, 0xee, 0x6e, 0x51, 0x14, 0x5e, 0x8e, - 0x89, 0x08, 0xe2, 0x10, 0x2f, 0xbd, 0x40, 0xa4, 0xfd, 0x89, 0x48, 0x18, 0x9f, 0xf4, 0x6d, 0x50, - 0xb2, 0x4c, 0x0b, 0xd9, 0x37, 0xc9, 0x7d, 0x85, 0x29, 0xe3, 0x3a, 0x0e, 0xac, 0x44, 0xf7, 0xc5, - 0xcd, 0xb8, 0x2c, 0xd0, 0xb1, 0xe0, 0xaa, 0xd2, 0xc8, 0x67, 0x89, 0xa5, 0xbb, 0x9f, 0x1a, 0xe4, - 0xdf, 0x03, 0xd4, 0x83, 0x85, 0x31, 0x1f, 0x67, 0x73, 0x54, 0x9a, 0x3e, 0x25, 0xdb, 0x31, 0x0f, - 0x92, 0x79, 0x88, 0x63, 0xcc, 0x91, 0xeb, 0x71, 0x6d, 0xdd, 0x69, 0x40, 0xa3, 0xd7, 0xf1, 0xff, - 0xab, 0xe0, 0xfd, 0x12, 0xad, 0xe9, 0x74, 0x87, 0x38, 0x0b, 0x9e, 0x42, 0x99, 0xa3, 0x5c, 0x22, - 0xae, 0x18, 0xe2, 0xff, 0x15, 0x7e, 0x66, 0xe0, 0x9a, 0xe9, 0xce, 0x08, 0x5d, 0x32, 0xb2, 0xf0, - 0x71, 0x41, 0x3a, 0x79, 0xa4, 0xc6, 0x19, 0xd3, 0x53, 0x53, 0x78, 0x7d, 0xf8, 0xea, 0xdb, 0x8f, - 0xef, 0x5f, 0x1b, 0xbb, 0x74, 0xe7, 0x7c, 0x8a, 0x90, 0x47, 0x0a, 0x4a, 0x0c, 0x24, 0x26, 0x4c, - 0xc7, 0x39, 0x82, 0x16, 0xa0, 0xa7, 0x08, 0x75, 0x55, 0x08, 0x31, 0x8a, 0x79, 0x5c, 0x76, 0x0f, - 0x4a, 0x0b, 0x89, 0x9e, 0xdf, 0xce, 0x23, 0x75, 0xca, 0xf4, 0xd4, 0xbd, 0xb8, 0xd6, 0xbb, 0x8f, - 0x2a, 0x13, 0x5c, 0x21, 0xdd, 0x23, 0x9d, 0x05, 0xbd, 0xaa, 0xd9, 0x33, 0x35, 0x5d, 0x0a, 0xe7, - 0x4b, 0xd2, 0x10, 0x32, 0xcd, 0x1e, 0x80, 0x90, 0xc0, 0xca, 0x22, 0x6c, 0x9e, 0x68, 0xcf, 0xaf, - 0x99, 0xee, 0x97, 0x06, 0xa1, 0x67, 0x7f, 0xb6, 0xa1, 0x6b, 0xce, 0x57, 0x7e, 0xdb, 0xf9, 0x25, - 0xd9, 0x18, 0x9c, 0x8e, 0x96, 0xc6, 0xb1, 0x86, 0x52, 0x0a, 0x69, 0x17, 0x3f, 0xf4, 0x8c, 0x62, - 0x8f, 0xde, 0x1b, 0x70, 0x30, 0x71, 0x10, 0x41, 0x30, 0x97, 0x18, 0x82, 0x42, 0xad, 0x63, 0x3e, - 0xb9, 0x66, 0xd7, 0xf3, 0x2d, 0x99, 0xde, 0x25, 0x9b, 0xe6, 0x31, 0x4e, 0x51, 0x29, 0x36, 0x41, - 0xeb, 0xcf, 0xff, 0xcb, 0x04, 0x8f, 0x6c, 0xcc, 0xfd, 0xdc, 0x24, 0x5b, 0x07, 0xa8, 0x7d, 0xcc, - 0x84, 0xac, 0x27, 0x76, 0xd3, 0x75, 0x44, 0x42, 0x42, 0x31, 0x8d, 0x83, 0x29, 0x14, 0x08, 0xd2, - 0x4a, 0xd4, 0x4c, 0xfa, 0x86, 0xac, 0xea, 0xab, 0xac, 0x2a, 0x3b, 0x7c, 0x66, 0x14, 0x1e, 0xd1, - 0x7e, 0xa9, 0x60, 0x73, 0xa1, 0x84, 0x4b, 0x2a, 0x47, 0x0c, 0xa1, 0x87, 0xde, 0xc4, 0x83, 0xa3, - 0x93, 0xe3, 0xd1, 0xf9, 0x89, 0x3f, 0x3a, 0x3e, 0x18, 0xef, 0x0d, 0x46, 0x87, 0xef, 0xef, 0xfb, - 0x46, 0x84, 0x3e, 0x24, 0xad, 0x48, 0xc8, 0x94, 0x69, 0xa7, 0x69, 0xe4, 0x1c, 0x23, 0x47, 0xe9, - 0xd6, 0x6b, 0x13, 0x05, 0x43, 0x9e, 0xea, 0x34, 0xf1, 0xab, 0x3c, 0x7a, 0x9b, 0xac, 0x07, 0x49, - 0x5c, 0x7e, 0x48, 0x71, 0xe8, 0xac, 0x99, 0xd6, 0x3b, 0x36, 0x30, 0x0a, 0xe9, 0x2d, 0xd2, 0x09, - 0xd9, 0xd5, 0x98, 0xb3, 0x14, 0x9d, 0x96, 0xc1, 0xda, 0x21, 0xbb, 0x3a, 0x66, 0x29, 0xd2, 0x6d, - 0xd2, 0x8e, 0x12, 0x51, 0x94, 0xac, 0xb6, 0x41, 0x5a, 0xe5, 0xdf, 0x51, 0x48, 0x67, 0x84, 0x64, - 0x4c, 0xb2, 0x14, 0x35, 0x4a, 0xe5, 0xac, 0x42, 0xb3, 0xb7, 0xf1, 0x78, 0xd3, 0x7e, 0xd4, 0xde, - 0xbb, 0xb7, 0x87, 0xfb, 0x3c, 0x1f, 0x0e, 0x8d, 0xab, 0xe7, 0x74, 0xd7, 0xce, 0x13, 0x7e, 0xe5, - 0x7b, 0xe5, 0xe4, 0x14, 0x42, 0x88, 0x19, 0xf2, 0x10, 0x04, 0x37, 0x3b, 0x33, 0x03, 0x10, 0x91, - 0x79, 0xdb, 0x99, 0x78, 0xfe, 0x52, 0x11, 0xf7, 0x63, 0x83, 0xfc, 0xb3, 0xb4, 0x9d, 0xea, 0x3c, - 0x28, 0x59, 0x2d, 0xcf, 0xc9, 0xae, 0xc6, 0x37, 0x6f, 0xda, 0x25, 0x1d, 0x8d, 0x69, 0x96, 0x30, - 0xbd, 0xd8, 0x73, 0xfd, 0x9f, 0xbe, 0x24, 0x9d, 0xea, 0x04, 0x94, 0xd3, 0x84, 0x66, 0x6f, 0x7d, - 0xe8, 0x1a, 0x9f, 0x77, 0x68, 0x77, 0xdf, 0x9e, 0x93, 0x84, 0x82, 0x49, 0x5e, 0x5e, 0xd2, 0x22, - 0xd1, 0xf3, 0x6b, 0xce, 0x87, 0x96, 0xe9, 0xf1, 0xc9, 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xeb, - 0x3c, 0x03, 0x97, 0x4d, 0x05, 0x00, 0x00, +func init() { proto.RegisterFile("artifacts.proto", fileDescriptor_artifacts_a25917c1f4a78cbd) } + +var fileDescriptor_artifacts_a25917c1f4a78cbd = []byte{ + // 734 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x54, 0xdf, 0x6e, 0xfb, 0x34, + 0x14, 0x56, 0xd6, 0xae, 0x7f, 0x3c, 0xa6, 0x6d, 0x46, 0xb0, 0x50, 0x90, 0xb0, 0x82, 0x84, 0x02, + 0x42, 0x29, 0x7f, 0x24, 0x98, 0x26, 0x40, 0xb4, 0xda, 0x98, 0x2a, 0xf6, 0x0f, 0xaf, 0x42, 0x42, + 0xbb, 0x88, 0xbc, 0xe4, 0xa4, 0x89, 0x94, 0xd8, 0xa9, 0xed, 0xa6, 0xdb, 0x35, 0x8f, 0xc1, 0x83, + 0xf0, 0x02, 0x3c, 0x09, 0xdc, 0xf0, 0x10, 0x5c, 0xa0, 0xd8, 0x69, 0xd6, 0xdd, 0x4d, 0x5c, 0xfc, + 0xae, 0xea, 0x9c, 0x73, 0xbe, 0xcf, 0xe7, 0x7c, 0xe7, 0x73, 0xd1, 0x01, 0x93, 0x3a, 0x4b, 0x58, + 0xa4, 0x55, 0x50, 0x4a, 0xa1, 0x05, 0xde, 0x35, 0x3f, 0xa3, 0xd3, 0xf5, 0x7a, 0x1d, 0x54, 0x90, + 0x8b, 0x28, 0x8b, 0xe1, 0x31, 0x88, 0x44, 0x31, 0x5e, 0x88, 0x9c, 0xf1, 0xc5, 0xd8, 0x06, 0x25, + 0x2b, 0xb5, 0x90, 0x63, 0x53, 0x3c, 0x56, 0x50, 0x30, 0xae, 0xb3, 0xc8, 0x52, 0x8c, 0xbe, 0x7b, + 0x1d, 0x96, 0x45, 0x3a, 0x13, 0x5c, 0x35, 0x1c, 0xd5, 0x32, 0xb7, 0x70, 0xef, 0x1f, 0x07, 0xbd, + 0x7d, 0x01, 0x7a, 0xb2, 0x69, 0x8c, 0xc2, 0x72, 0x05, 0x4a, 0xe3, 0xaf, 0xd1, 0x71, 0xc6, 0xa3, + 0x7c, 0x15, 0x43, 0x08, 0x15, 0x70, 0x1d, 0xb6, 0xad, 0xbb, 0x0e, 0x71, 0xfc, 0x01, 0x7d, 0xa7, + 0x49, 0x9f, 0xd7, 0xd9, 0x16, 0x8e, 0x4f, 0x90, 0xbb, 0xc1, 0x29, 0x90, 0x15, 0xc8, 0x2d, 0xe0, + 0x8e, 0x01, 0xbe, 0xdb, 0xe4, 0xef, 0x4c, 0xfa, 0x19, 0xf9, 0x21, 0xda, 0x53, 0xc0, 0x64, 0x94, + 0x86, 0x1a, 0x64, 0xe1, 0x76, 0x88, 0xe3, 0x0f, 0x29, 0xb2, 0xa1, 0x39, 0xc8, 0x02, 0x7f, 0x8a, + 0x8e, 0xf8, 0xaa, 0x78, 0x00, 0x19, 0x8a, 0x24, 0x94, 0xa0, 0x56, 0xb9, 0x56, 0x6e, 0x97, 0x38, + 0x7e, 0x97, 0x1e, 0xd8, 0xc4, 0x4d, 0x42, 0x6d, 0x18, 0x63, 0xd4, 0xd5, 0x4f, 0x25, 0xb8, 0xbb, + 0x86, 0xc5, 0x9c, 0xbd, 0x25, 0xc2, 0x5b, 0x93, 0x6e, 0x06, 0xbd, 0x47, 0x83, 0x2a, 0x51, 0x61, + 0xc9, 0x74, 0x6a, 0x26, 0x1b, 0x4e, 0x7f, 0xf8, 0xeb, 0xdf, 0xbf, 0xff, 0x74, 0x4e, 0xf1, 0xc9, + 0x3c, 0x05, 0x52, 0x25, 0x8a, 0xd4, 0x39, 0x22, 0x21, 0x67, 0x3a, 0xab, 0x80, 0x68, 0x41, 0x74, + 0x0a, 0xa4, 0x1d, 0x8b, 0xc4, 0x90, 0x64, 0x3c, 0xab, 0xe5, 0x25, 0x4a, 0x0b, 0x09, 0x01, 0xed, + 0x57, 0x89, 0xba, 0x65, 0x3a, 0xf5, 0xee, 0x5f, 0x88, 0x4b, 0x41, 0x95, 0x82, 0x2b, 0xc0, 0x67, + 0x68, 0xb0, 0x81, 0x37, 0x77, 0xfa, 0xe6, 0x4e, 0x0f, 0x93, 0xf9, 0x16, 0x35, 0x89, 0x99, 0x66, + 0x9f, 0x11, 0x21, 0x09, 0xab, 0x2f, 0x61, 0xab, 0x5c, 0x07, 0xb4, 0x45, 0x7a, 0x7f, 0x38, 0x08, + 0xdf, 0xbd, 0xd9, 0x81, 0x5e, 0x74, 0xbe, 0xf3, 0xbf, 0x3b, 0x7f, 0x44, 0x7b, 0x93, 0xdb, 0xd9, + 0x96, 0x1c, 0xbb, 0x20, 0xa5, 0x90, 0xd6, 0x59, 0xd3, 0xc0, 0x30, 0xfa, 0xf8, 0xe3, 0x09, 0x27, + 0x26, 0x4e, 0x44, 0x14, 0xad, 0x24, 0xc4, 0x44, 0x81, 0xd6, 0x19, 0x5f, 0xbc, 0x68, 0x37, 0xa0, + 0x16, 0x8c, 0x3f, 0x42, 0xfb, 0xe6, 0x10, 0x16, 0xa0, 0x14, 0x5b, 0x80, 0xed, 0x8f, 0xbe, 0x65, + 0x82, 0x57, 0x36, 0xe6, 0xfd, 0xde, 0x41, 0x87, 0x17, 0xa0, 0x29, 0x94, 0x42, 0xb6, 0x8a, 0xbd, + 0x76, 0x1d, 0x89, 0x90, 0x64, 0x9d, 0x66, 0x51, 0x4a, 0xd6, 0x40, 0xa4, 0xa5, 0x68, 0x91, 0xf8, + 0xa7, 0xc6, 0x72, 0x56, 0x96, 0x6f, 0x0c, 0xc3, 0x17, 0x78, 0x5c, 0x33, 0xd8, 0x5a, 0x52, 0xa7, + 0x6b, 0x28, 0x07, 0x88, 0x89, 0x0f, 0xc1, 0x22, 0x20, 0x57, 0x37, 0xd7, 0xb3, 0xf9, 0x0d, 0x9d, + 0x5d, 0x5f, 0x84, 0x67, 0x93, 0xd9, 0xe5, 0xaf, 0x9f, 0x58, 0xaf, 0xe2, 0xcf, 0x51, 0x2f, 0x11, + 0xb2, 0x60, 0xda, 0xbe, 0x83, 0xa9, 0x6b, 0xe8, 0x30, 0x3e, 0xfc, 0xd1, 0x44, 0x89, 0x01, 0xa7, + 0xba, 0xc8, 0x69, 0x53, 0x87, 0xdf, 0x47, 0xc3, 0x28, 0xcf, 0xea, 0x97, 0x9a, 0xc5, 0x8d, 0xed, + 0x07, 0x36, 0x30, 0x8b, 0xf1, 0x7b, 0x68, 0x10, 0xb3, 0xa7, 0x90, 0xb3, 0x02, 0xdc, 0x9e, 0xc9, + 0xf5, 0x63, 0xf6, 0x74, 0xcd, 0x0a, 0xc0, 0xc7, 0xa8, 0x9f, 0xe4, 0x62, 0x5d, 0xa3, 0xfa, 0x26, + 0xd3, 0xab, 0x3f, 0x67, 0x31, 0x5e, 0x22, 0x54, 0x32, 0xc9, 0x0a, 0xd0, 0x20, 0xeb, 0x77, 0xd6, + 0xf1, 0xf7, 0xbe, 0xdc, 0xb7, 0xff, 0x1a, 0xc1, 0x2f, 0x3f, 0x5f, 0x9e, 0xf3, 0x6a, 0x3a, 0x35, + 0x5d, 0x7d, 0x8b, 0x4f, 0xad, 0x9e, 0xe4, 0xb9, 0x3e, 0xa8, 0x95, 0x53, 0x40, 0x62, 0x28, 0x81, + 0xc7, 0x44, 0x70, 0xb3, 0x33, 0x23, 0x80, 0x48, 0xcc, 0xd9, 0x6a, 0x12, 0xd0, 0xad, 0x4b, 0xbc, + 0xdf, 0x1c, 0x74, 0xb4, 0xb5, 0x9d, 0xc6, 0x1e, 0x18, 0x75, 0x6b, 0x3b, 0xd9, 0xd5, 0x50, 0x73, + 0xc6, 0x23, 0x34, 0xd0, 0x50, 0x94, 0x39, 0xd3, 0x9b, 0x3d, 0xb7, 0xdf, 0xf8, 0x7b, 0x34, 0x68, + 0x2c, 0xa0, 0xdc, 0x0e, 0xe9, 0xf8, 0xc3, 0xa9, 0x67, 0xfa, 0xfc, 0x00, 0x8f, 0xce, 0xad, 0x9d, + 0x24, 0x59, 0x33, 0xc9, 0x6b, 0x27, 0x6d, 0x0a, 0x03, 0xda, 0x62, 0x1e, 0x7a, 0x66, 0xc6, 0xaf, + 0xfe, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x9b, 0xc2, 0x4e, 0x98, 0xae, 0x05, 0x00, 0x00, } diff --git a/api/proto/artifacts.proto b/api/proto/artifacts.proto index 13c85b27bde..d557c8a2bda 100644 --- a/api/proto/artifacts.proto +++ b/api/proto/artifacts.proto @@ -7,12 +7,18 @@ import "www.velocidex.com/golang/velociraptor/actions/proto/vql.proto"; package proto; message GetArtifactsRequest { + // Deprecated bool include_event_artifacts = 1; bool include_server_artifacts = 2; + + string search_term = 3; + uint64 number_of_results = 4; + string type = 5; } message GetArtifactRequest { + // Deprecated. string vfs_path = 1 [(sem_type) = { description: "The vfs path relative to the artifacts definition store." }]; diff --git a/api/proxy.go b/api/proxy.go index 26f8d197be4..5ad27b0e248 100644 --- a/api/proxy.go +++ b/api/proxy.go @@ -34,10 +34,17 @@ import ( "google.golang.org/grpc/metadata" api_proto "www.velocidex.com/golang/velociraptor/api/proto" "www.velocidex.com/golang/velociraptor/constants" + "www.velocidex.com/golang/velociraptor/crypto" "www.velocidex.com/golang/velociraptor/grpc_client" "www.velocidex.com/golang/velociraptor/logging" ) +var ( + // An internal cert used to make grpc calles from the gw proxy + // to the grpc service. + api_handle_certificate *crypto.CertBundle +) + func AddProxyMux(config_obj *api_proto.Config, mux *http.ServeMux) error { logger := logging.Manager.GetLogger(config_obj, &logging.GUIComponent) @@ -225,11 +232,23 @@ func GetAPIHandler( }), ) + // Generate internal certificates so we can tell when a call + // came from internal. + if api_handle_certificate == nil { + new_cert, err := crypto.GenerateServerCert( + config_obj, constants.GRPC_GW_CLIENT_NAME) + if err != nil { + return nil, err + } + + api_handle_certificate = new_cert + } + // We use the Frontend's certificate because this connection // represents an internal connection. cert, err := tls.X509KeyPair( - []byte(config_obj.Frontend.Certificate), - []byte(config_obj.Frontend.PrivateKey)) + []byte(api_handle_certificate.Cert), + []byte(api_handle_certificate.PrivateKey)) if err != nil { return nil, err } diff --git a/constants/constants.go b/constants/constants.go index 6435e41fa32..2f830efb587 100644 --- a/constants/constants.go +++ b/constants/constants.go @@ -50,7 +50,8 @@ const ( // Messages to the client which we dont care about their responses. IgnoreResponseState = uint64(101) - FRONTEND_NAME = "VelociraptorServer" + FRONTEND_NAME = "VelociraptorServer" + GRPC_GW_CLIENT_NAME = "GRPC_GW" ServerMonitoringFlowURN = "aff4:/config/server_monitoring.json" ) diff --git a/glob/glob.go b/glob/glob.go index d4ed05455a1..afa7960e13d 100644 --- a/glob/glob.go +++ b/glob/glob.go @@ -324,6 +324,7 @@ func (self Globber) ExpandWithContext( if !ok { break search_subdir } + output_chan <- f } } diff --git a/gui/static/angular-components/artifact/artifact.js b/gui/static/angular-components/artifact/artifact.js index a0c0ced4fab..8ae8eaf71e9 100644 --- a/gui/static/angular-components/artifact/artifact.js +++ b/gui/static/angular-components/artifact/artifact.js @@ -11,6 +11,7 @@ const {ArtifactsParamsFormDirective} = goog.require('grrUi.artifact.artifactsPar const {LineChartDirective} = goog.require('grrUi.artifact.lineChartDirective'); const {TimelineDirective} = goog.require('grrUi.artifact.timelineDirective'); const {ReportingDirective} = goog.require('grrUi.artifact.reportingDirective'); +const {SearchArtifactDirective} = goog.require('grrUi.artifact.searchArtifactDirective'); const {SyntaxHighlightDirective} = goog.require('grrUi.artifact.syntaxHighlightDirective'); const {coreModule} = goog.require('grrUi.core.core'); const {formsModule} = goog.require('grrUi.forms.forms'); @@ -39,6 +40,9 @@ exports.artifactModule.directive( exports.artifactModule.directive( ReportingDirective.directive_name, ReportingDirective); +exports.artifactModule.directive( + SearchArtifactDirective.directive_name, + SearchArtifactDirective); exports.artifactModule.directive( SyntaxHighlightDirective.directive_name, SyntaxHighlightDirective); diff --git a/gui/static/angular-components/artifact/artifacts-parameters-form-directive.js b/gui/static/angular-components/artifact/artifacts-parameters-form-directive.js index 23a5e99e713..ab94a44364e 100644 --- a/gui/static/angular-components/artifact/artifacts-parameters-form-directive.js +++ b/gui/static/angular-components/artifact/artifacts-parameters-form-directive.js @@ -15,28 +15,21 @@ goog.module.declareLegacyNamespace(); const ArtifactsParamsFormController = function($scope, $rootScope) { /** @private {!angular.Scope} */ this.scope_ = $scope; - - /** @private {!angular.Scope} */ - this.rootScope_ = $rootScope; }; ArtifactsParamsFormController.prototype.addItem = function() { - var descriptor = this.rootScope_["selectedArtifact"]; - if (angular.isUndefined(this.scope_.value.env)) { - this.scope_.value.env = [] - } - - if (angular.isDefined(descriptor)) { - for (var i=0; i
+
+
+ + +
+ + + + + + + + + + + + + + +
+
+ {$ ::name $} +
+
+ Use "Add" button or double-click to add artifacts to the list. +
+
+ +
+ + +
+ +
+
+
+ + +
+ +
+ + + + + + +
+ {{ k }} + + + +
+ + diff --git a/gui/static/angular-components/flow/new_artifact_collection-directive.js b/gui/static/angular-components/flow/new_artifact_collection-directive.js index bc8c0397f83..854835be114 100644 --- a/gui/static/angular-components/flow/new_artifact_collection-directive.js +++ b/gui/static/angular-components/flow/new_artifact_collection-directive.js @@ -1,10 +1,8 @@ 'use strict'; goog.module('grrUi.flow.newArtifactCollectionDirective'); -goog.module.declareLegacyNamespace(); const {ApiService, stripTypeInfo} = goog.require('grrUi.core.apiService'); -const {ReflectionService} = goog.require('grrUi.core.reflectionService'); /** @@ -13,11 +11,10 @@ const {ReflectionService} = goog.require('grrUi.core.reflectionService'); * @constructor * @param {!angular.Scope} $scope * @param {!ApiService} grrApiService - * @param {!ReflectionService} grrReflectionService * @ngInject */ const NewArtifactCollectionController = function( - $scope, grrApiService, grrReflectionService) { + $scope, grrApiService) { /** @private {!angular.Scope} */ this.scope_ = $scope; @@ -27,19 +24,6 @@ const NewArtifactCollectionController = function( /** @private {!ApiService} */ this.grrApiService_ = grrApiService; - /** @private {!ReflectionService} */ - this.grrReflectionService_ = grrReflectionService; - - /** @type {Object} */ - this.flowArguments = { - "@type": "type.googleapis.com/proto.ArtifactCollectorArgs" - }; - - /** @type {Object} */ - this.flowRunnerArguments = { - "flow_name": "ArtifactCollector", - }; - /** @type {boolean} */ this.requestSent = false; @@ -50,7 +34,12 @@ const NewArtifactCollectionController = function( this.responseData; /** @type {boolean} */ - this.flowFormHasErrors; + this.flowFormHasErrors; + + this.params = {}; + this.names = []; + this.ops_per_second; + this.timeout; }; @@ -69,24 +58,39 @@ NewArtifactCollectionController.prototype.resolve = function() { * @export */ NewArtifactCollectionController.prototype.startClientFlow = function() { - var clientIdComponents = this.scope_['clientId'].split('/'); - var clientId; - if (clientIdComponents[0] == 'aff4:') { - clientId = clientIdComponents[1]; - } else { - clientId = clientIdComponents[0]; - } + var self = this; + var clientId = this.scope_['clientId']; + var env = []; + for (var k in self.params) { + if (self.params.hasOwnProperty(k)) { + env.push({key: k, value: self.params[k]}); + } + } + + this.flowRunnerArguments = { + client_id: clientId, + "flow_name": "ArtifactCollector", + args: { + "@type": "type.googleapis.com/proto.ArtifactCollectorArgs", + artifacts: { + names: this.names, + }, + parameters: { + env: env, + }, + ops_per_second: this.ops_per_second, + timeout: this.timeout, + } + }; - this.flowRunnerArguments.client_id = clientId; - this.flowRunnerArguments.args = this.flowArguments; - this.grrApiService_.post( - 'v1/LaunchFlow', - this.flowRunnerArguments).then(function success(response) { - this.responseData = response['data']; - }.bind(this), function failure(response) { - this.responseError = response['data']['error'] || 'Unknown error'; - }.bind(this)); - this.requestSent = true; + this.grrApiService_.post( + 'v1/LaunchFlow', + this.flowRunnerArguments).then(function success(response) { + this.responseData = response['data']; + }.bind(this), function failure(response) { + this.responseError = response['data']['error'] || 'Unknown error'; + }.bind(this)); + this.requestSent = true; }; diff --git a/gui/static/angular-components/flow/new_artifact_collection.html b/gui/static/angular-components/flow/new_artifact_collection.html index 6974dee6fc6..d1b907429b9 100644 --- a/gui/static/angular-components/flow/new_artifact_collection.html +++ b/gui/static/angular-components/flow/new_artifact_collection.html @@ -3,10 +3,9 @@ on-reject="onReject()"> - - + +
- Creating hunt... + Launching new Artifacts Collection...
@@ -32,9 +31,10 @@

Flow arguments:
- +

diff --git a/gui/static/angular-components/hunt/new-hunt-wizard/form-directive.js b/gui/static/angular-components/hunt/new-hunt-wizard/form-directive.js index 955b2ac7a80..315f1ab92e6 100644 --- a/gui/static/angular-components/hunt/new-hunt-wizard/form-directive.js +++ b/gui/static/angular-components/hunt/new-hunt-wizard/form-directive.js @@ -26,6 +26,11 @@ const FormController = function($scope, grrReflectionService, grrApiService) { /** @private {!ApiService} */ this.grrApiService_ = grrApiService; + this.names = []; + this.params = {}; + this.ops_per_second; + this.timeout; + if (angular.isUndefined(this.scope_['createHuntArgs'])) { this.scope_['createHuntArgs'] = { start_request: { @@ -46,15 +51,27 @@ const FormController = function($scope, grrReflectionService, grrApiService) { * @export */ FormController.prototype.sendRequest = function() { - this.grrApiService_.post( - 'v1/CreateHunt', - this.scope_['createHuntArgs']) - .then(function resolve(response) { - this.serverResponse = response; - }.bind(this), function reject(response) { - this.serverResponse = response; - this.serverResponse['error'] = true; - }.bind(this)); + var self = this; + var env = []; + for (var k in self.params) { + if (self.params.hasOwnProperty(k)) { + env.push({key: k, value: self.params[k]}); + } + } + + var createHuntArgs = this.scope_['createHuntArgs']; + createHuntArgs.start_request.args.artifacts = {names: this.names}; + createHuntArgs.start_request.args.parameters = {env: env}; + createHuntArgs.start_request.args.ops_per_second = this.ops_per_second; + createHuntArgs.start_request.args.timeout = this.timeout; + + this.grrApiService_.post('v1/CreateHunt', createHuntArgs) + .then(function resolve(response) { + this.serverResponse = response; + }.bind(this), function reject(response) { + this.serverResponse = response; + this.serverResponse['error'] = true; + }.bind(this)); }; diff --git a/gui/static/angular-components/hunt/new-hunt-wizard/form.html b/gui/static/angular-components/hunt/new-hunt-wizard/form.html index 10d8b53e901..e215bed07e2 100644 --- a/gui/static/angular-components/hunt/new-hunt-wizard/form.html +++ b/gui/static/angular-components/hunt/new-hunt-wizard/form.html @@ -2,9 +2,10 @@ - - + + + diff --git a/gui/static/angular-components/routing/routing.js b/gui/static/angular-components/routing/routing.js index 74cd95ffa83..6e1205062d4 100644 --- a/gui/static/angular-components/routing/routing.js +++ b/gui/static/angular-components/routing/routing.js @@ -97,7 +97,6 @@ exports.routingModule // // States when a client is selected. // - .state('client', { url: '/clients/:clientId', redirectTo: 'client.hostInfo', @@ -121,6 +120,11 @@ exports.routingModule template: '', title: 'Collect Artifacts' }) + .state('client.searchArtifacts', { + url: '/search-artifacts', + template: '', + title: 'Search for Artifact', + }) .state('client.vfs', { url: '/vfs/{path:pathWithUnescapedSlashes}?version&mode&tab', template: '', diff --git a/gui/static/angular-components/sidebar/navigator.html b/gui/static/angular-components/sidebar/navigator.html index f718ebec51d..2a3011299e4 100644 --- a/gui/static/angular-components/sidebar/navigator.html +++ b/gui/static/angular-components/sidebar/navigator.html @@ -24,6 +24,11 @@ > Collected Artifacts +
  • + Search Artifacts +
  • diff --git a/gui/static/css/base.scss b/gui/static/css/base.scss index 7ad2e2235b0..df5dce74b11 100644 --- a/gui/static/css/base.scss +++ b/gui/static/css/base.scss @@ -328,6 +328,7 @@ div[ui-view] { bottom: 5em; margin-left: 5%; position: absolute; + height: 80%; } } @@ -1069,3 +1070,9 @@ flot-tooltip { .report-selector { padding-bottom: 40px; } + +.artifact-description { + height: 420px; + overflow-y: auto; + overflow-x: hidden; +} diff --git a/services/hunt_manager.go b/services/hunt_manager.go index e0ce1b20dd6..dc0e0d014a1 100644 --- a/services/hunt_manager.go +++ b/services/hunt_manager.go @@ -150,7 +150,7 @@ func (self *HuntManager) ProcessRow( request := &flows_proto.FlowRunnerArgs{ ClientId: participation_row.ClientId, - Creator: participation_row.HuntId, + Creator: path.Base(participation_row.HuntId), } // Get hunt information about this hunt. diff --git a/services/server_monitoring.go b/services/server_monitoring.go index 2a0805e5285..11fab8a46f5 100644 --- a/services/server_monitoring.go +++ b/services/server_monitoring.go @@ -161,54 +161,44 @@ func (self *EventTable) GetWriter( defer closer() - for { - select { - case <-ctx.Done(): - return - - case row, ok := <-row_chan: - if !ok { - return + for row := range row_chan { + now := time.Now() + log_path := path.Join( + "server_artifacts", name, + fmt.Sprintf("%d-%02d-%02d.csv", now.Year(), + now.Month(), now.Day())) + + // We need to rotate the log file. + if log_path != last_log { + closer() + + fd, err = file_store_factory.WriteFile(log_path) + if err != nil { + fmt.Printf("Error: %v\n", err) + continue } - now := time.Now() - log_path := path.Join( - "server_artifacts", name, - fmt.Sprintf("%d-%02d-%02d.csv", now.Year(), - now.Month(), now.Day())) - - // We need to rotate the log file. - if log_path != last_log { - closer() - - fd, err = file_store_factory.WriteFile(log_path) - if err != nil { - fmt.Printf("Error: %v\n", err) - continue - } - - writer, err = csv.GetCSVWriter(scope, fd) - if err != nil { - continue - } + writer, err = csv.GetCSVWriter(scope, fd) + if err != nil { + continue } + } - if columns == nil { - columns = scope.GetMembers(row) - } + if columns == nil { + columns = scope.GetMembers(row) + } - dict_row, ok := row.(*vfilter.Dict) - if !ok { - dict_row := vfilter.NewDict() - for _, column := range columns { - value, pres := scope.Associative(row, column) - if pres { - dict_row.Set(column, value) - } + dict_row, ok := row.(*vfilter.Dict) + if !ok { + dict_row := vfilter.NewDict() + for _, column := range columns { + value, pres := scope.Associative(row, column) + if pres { + dict_row.Set(column, value) } } - writer.Write(dict_row) } + writer.Write(dict_row) } }() diff --git a/vql/functions/hash.go b/vql/functions/hash.go index 3a4be2746d6..a0afd83afd6 100644 --- a/vql/functions/hash.go +++ b/vql/functions/hash.go @@ -59,11 +59,15 @@ func (self *HashFunction) Call(ctx context.Context, return false } + if arg.Path == "" { + return vfilter.Null{} + } + buf := make([]byte, 4*1024*1024) // 4Mb chunks fs := glob.GetAccessor(arg.Accessor, ctx) file, err := fs.Open(arg.Path) if err != nil { - scope.Log(err.Error()) + scope.Log("hash %s: %v", arg.Path, err.Error()) return false } defer file.Close()