Skip to content

Commit 9e12a1a

Browse files
committed
Refactoring:
- hids package - hook functions taking hids as first parameter to easily access config from hooks - removed global variables shared between hooks and HIDS - manager command handler moved from api package to hids to easily access hids config Fixed issues: - Implement actionnable rules: #28 - Implement event count: #29 - Enrich events with signature information: #32 - Automatic canary folder management: #33 - Ability to configure audit policies from WHIDS config: #34 - Set File System Audit ACLs from config: #35 - Generate IR ready reports on detections: #36 - Dump process tree: #38 - Enrich event with Gene process scoring: #40 - Add Admin API to list and download artifacts dumped: #42 - Directory listing command: #44 - Implement hash command: #45 - Implement osquery command: #46 - Implement terminate command: #47 - Implement stat command: #48 - Implement walk command: #49 - Implement find command: #50 - Implement report command: #51 - Implement processes command: #52 - Implement drivers command: #53
1 parent 26f3610 commit 9e12a1a

36 files changed

+3515
-2661
lines changed

api/adminapi_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ func TestAdminAPIPostCommand(t *testing.T) {
182182
}
183183
r := post(format("%s/%s/command", AdmAPIEndpointsPath, euuid), JSON(ca))
184184
failOnAdminAPIError(t, r)
185-
if err := c.ExecuteCommand(); err != nil {
185+
if _, err := c.ExecuteCommand(); err != nil {
186186
t.Errorf("Failed to execute command: %s", err)
187187
t.FailNow()
188188
}
@@ -214,7 +214,7 @@ func TestAdminAPIGetCommandField(t *testing.T) {
214214
r := post(format("%s/%s/command", AdmAPIEndpointsPath, euuid), JSON(ca))
215215
failOnAdminAPIError(t, r)
216216

217-
if err := c.ExecuteCommand(); err != nil {
217+
if _, err := c.ExecuteCommand(); err != nil {
218218
t.Errorf("Failed to execute command: %s", err)
219219
t.FailNow()
220220
}
@@ -289,7 +289,7 @@ func TestAdminAPIGetEndpointReport(t *testing.T) {
289289
t.Logf("Failed to prepare request: %s", err)
290290
t.FailNow()
291291
}
292-
mc.httpClient.Do(r)
292+
mc.HTTPClient.Do(r)
293293
}
294294

295295
time.Sleep(1 * time.Second)
@@ -340,7 +340,7 @@ func TestAdminAPIGetEndpointLogs(t *testing.T) {
340340
t.Logf("Failed to prepare request: %s", err)
341341
t.FailNow()
342342
}
343-
mc.httpClient.Do(r)
343+
mc.HTTPClient.Do(r)
344344
}
345345

346346
time.Sleep(1 * time.Second)
@@ -439,7 +439,7 @@ func TestAdminAPIGetEndpointAlerts(t *testing.T) {
439439
t.Logf("Failed to prepare request: %s", err)
440440
t.FailNow()
441441
}
442-
mc.httpClient.Do(r)
442+
mc.HTTPClient.Do(r)
443443
}
444444

445445
time.Sleep(1 * time.Second)

api/client.go renamed to api/api_client.go

+73-84
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@ import (
44
"bytes"
55
"compress/gzip"
66
"context"
7-
"crypto/sha256"
87
"crypto/tls"
98
"crypto/x509"
10-
"encoding/hex"
119
"encoding/json"
1210
"fmt"
1311
"io"
@@ -81,7 +79,7 @@ func (cc *ClientConfig) Transport() http.RoundTripper {
8179
return c, err
8280
}
8381
}
84-
return c, fmt.Errorf("Server fingerprint not verified")
82+
return c, fmt.Errorf("server fingerprint not verified")
8583
},
8684
MaxIdleConns: 100,
8785
IdleConnTimeout: 90 * time.Second,
@@ -92,18 +90,17 @@ func (cc *ClientConfig) Transport() http.RoundTripper {
9290

9391
// ManagerClient structure definition
9492
type ManagerClient struct {
95-
httpClient http.Client
96-
config ClientConfig
97-
managerIP net.IP
93+
config ClientConfig
94+
ManagerIP net.IP
95+
96+
HTTPClient http.Client
9897
}
9998

10099
const (
101100
// UserAgent used by the client
102101
UserAgent = "Whids-API-Client/1.0"
103102
// Mega byte size
104103
Mega = 1 << 20
105-
// DefaultMaxUploadSize default maximum upload size
106-
DefaultMaxUploadSize = 100 * Mega
107104
)
108105

109106
var (
@@ -115,34 +112,25 @@ func init() {
115112
var err error
116113
Hostname, err = os.Hostname()
117114
if err != nil {
118-
id := data.Md5([]byte(fmt.Sprintf("%s", time.Now().Format(time.RFC3339Nano))))
115+
id := data.Md5([]byte(time.Now().Format(time.RFC3339Nano)))
119116
Hostname = fmt.Sprintf("HOST-%s", id)
120117
}
121118
}
122119

123-
// Sha256StringArray utility
124-
func Sha256StringArray(array []string) string {
125-
sha256 := sha256.New()
126-
for _, e := range array {
127-
sha256.Write([]byte(e))
128-
}
129-
return hex.EncodeToString(sha256.Sum(nil))
130-
}
131-
132120
// NewManagerClient creates a new Client to interface with the manager
133121
func NewManagerClient(c *ClientConfig) (*ManagerClient, error) {
134122

135123
tpt := c.Transport()
136124

137125
mc := &ManagerClient{
138-
httpClient: http.Client{Transport: tpt},
126+
HTTPClient: http.Client{Transport: tpt},
139127
config: *c,
140-
managerIP: c.ManagerIP(),
128+
ManagerIP: c.ManagerIP(),
141129
}
142130

143131
// host
144132
if mc.config.Host == "" {
145-
return nil, fmt.Errorf("Field \"host\" is missing from configuration")
133+
return nil, fmt.Errorf("field \"host\" is missing from configuration")
146134
}
147135
// protocol
148136
if mc.config.Proto == "" {
@@ -152,12 +140,12 @@ func NewManagerClient(c *ClientConfig) (*ManagerClient, error) {
152140
switch mc.config.Proto {
153141
case "http", "https":
154142
default:
155-
return nil, fmt.Errorf("Protocol not supported (only http(s))")
143+
return nil, fmt.Errorf("protocol not supported (only http(s))")
156144
}
157145

158146
// key
159147
if mc.config.Key == "" {
160-
return nil, fmt.Errorf("Field \"key\" is missing from configuration")
148+
return nil, fmt.Errorf("field \"key\" is missing from configuration")
161149
}
162150

163151
return mc, nil
@@ -208,7 +196,7 @@ func (m *ManagerClient) IsServerUp() bool {
208196
log.Errorf("IsServerUp cannot create server key request: %s", err)
209197
return false
210198
}
211-
resp, err := m.httpClient.Do(get)
199+
resp, err := m.HTTPClient.Do(get)
212200
if err != nil {
213201
log.Errorf("IsServerUp cannot issue server key request: %s", err)
214202
return false
@@ -229,7 +217,7 @@ func (m *ManagerClient) IsServerAuthenticated() (auth bool, up bool) {
229217
log.Errorf("IsServerAuthenticated cannot create server key request: %s", err)
230218
return false, false
231219
}
232-
resp, err := m.httpClient.Do(get)
220+
resp, err := m.HTTPClient.Do(get)
233221
if err != nil {
234222
log.Errorf("IsServerAuthenticated cannot issue server key request: %s", err)
235223
return false, false
@@ -266,15 +254,15 @@ func (m *ManagerClient) GetRulesSha256() (string, error) {
266254
return "", fmt.Errorf("GetRulesSha256 failed to prepare request: %s", err)
267255
}
268256

269-
resp, err := m.httpClient.Do(req)
257+
resp, err := m.HTTPClient.Do(req)
270258
if err != nil {
271-
return "", fmt.Errorf("GetRulesSha256 failed to issue HTTP request: %s", err)
259+
return "", fmt.Errorf("SetRulesSha256 failed to issue HTTP request: %s", err)
272260
}
273261

274262
if resp != nil {
275263
defer resp.Body.Close()
276264
if resp.StatusCode != 200 {
277-
return "", fmt.Errorf("Failed to retrieve rules sha256, unexpected HTTP status code %d", resp.StatusCode)
265+
return "", fmt.Errorf("failed to retrieve rules sha256, unexpected HTTP status code %d", resp.StatusCode)
278266
}
279267
sha256, err := ioutil.ReadAll(resp.Body)
280268
if err != nil {
@@ -296,15 +284,15 @@ func (m *ManagerClient) GetContainer(name string) ([]string, error) {
296284
return ctn, fmt.Errorf("GetContainer failed to prepare request: %s", err)
297285
}
298286

299-
resp, err := m.httpClient.Do(req)
287+
resp, err := m.HTTPClient.Do(req)
300288
if err != nil {
301289
return ctn, fmt.Errorf("GetContainer failed to issue HTTP request: %s", err)
302290
}
303291

304292
if resp != nil {
305293
defer resp.Body.Close()
306294
if resp.StatusCode != 200 {
307-
return ctn, fmt.Errorf("Failed to retrieve container, unexpected HTTP status code %d", resp.StatusCode)
295+
return ctn, fmt.Errorf("failed to retrieve container, unexpected HTTP status code %d", resp.StatusCode)
308296
}
309297
dec := json.NewDecoder(resp.Body)
310298
if err = dec.Decode(&ctn); err != nil {
@@ -325,15 +313,15 @@ func (m *ManagerClient) GetContainersList() ([]string, error) {
325313
return ctn, fmt.Errorf("GetContainersList failed to prepare request: %s", err)
326314
}
327315

328-
resp, err := m.httpClient.Do(req)
316+
resp, err := m.HTTPClient.Do(req)
329317
if err != nil {
330318
return ctn, fmt.Errorf("GetContainersList failed to issue HTTP request: %s", err)
331319
}
332320

333321
if resp != nil {
334322
defer resp.Body.Close()
335323
if resp.StatusCode != 200 {
336-
return ctn, fmt.Errorf("Failed to retrieve containers list, unexpected HTTP status code %d", resp.StatusCode)
324+
return ctn, fmt.Errorf("failed to retrieve containers list, unexpected HTTP status code %d", resp.StatusCode)
337325
}
338326
dec := json.NewDecoder(resp.Body)
339327
if err = dec.Decode(&ctn); err != nil {
@@ -353,15 +341,15 @@ func (m *ManagerClient) GetContainerSha256(name string) (string, error) {
353341
return "", fmt.Errorf("GetContainerSha256 failed to prepare request: %s", err)
354342
}
355343

356-
resp, err := m.httpClient.Do(req)
344+
resp, err := m.HTTPClient.Do(req)
357345
if err != nil {
358346
return "", fmt.Errorf("GetContainerSha256 failed to issue HTTP request: %s", err)
359347
}
360348

361349
if resp != nil {
362350
defer resp.Body.Close()
363351
if resp.StatusCode != 200 {
364-
return "", fmt.Errorf("Failed to retrieve container sha256, unexpected HTTP status code %d", resp.StatusCode)
352+
return "", fmt.Errorf("failed to retrieve container sha256, unexpected HTTP status code %d", resp.StatusCode)
365353
}
366354
sha256, err := ioutil.ReadAll(resp.Body)
367355
if err != nil {
@@ -381,7 +369,7 @@ func (m *ManagerClient) GetRules() (string, error) {
381369
return "", fmt.Errorf("GetRules failed to prepare request: %s", err)
382370
}
383371

384-
resp, err := m.httpClient.Do(req)
372+
resp, err := m.HTTPClient.Do(req)
385373
if err != nil {
386374
return "", fmt.Errorf("GetRules failed to issue HTTP request: %s", err)
387375
}
@@ -416,7 +404,7 @@ func (m *ManagerClient) PrepareFileUpload(path, guid, evthash, filename string)
416404
}
417405
return &fu, nil
418406
}
419-
return &fu, fmt.Errorf("Dump size above limit")
407+
return &fu, fmt.Errorf("dump size above limit")
420408
}
421409
return &fu, os.ErrNotExist
422410
}
@@ -446,7 +434,7 @@ func (m *ManagerClient) PostDump(f *FileUpload) error {
446434
return fmt.Errorf("PostDump failed to prepare request: %s", err)
447435
}
448436

449-
resp, err := m.httpClient.Do(req)
437+
resp, err := m.HTTPClient.Do(req)
450438
if err != nil {
451439
return fmt.Errorf("PostDump failed to issue HTTP request: %s", err)
452440
}
@@ -475,7 +463,7 @@ func (m *ManagerClient) PostLogs(r io.Reader) error {
475463
return fmt.Errorf("PostLogs failed to prepare request: %s", err)
476464
}
477465

478-
resp, err := m.httpClient.Do(req)
466+
resp, err := m.HTTPClient.Do(req)
479467
if err != nil {
480468
return fmt.Errorf("PostLogs failed to issue HTTP request: %s", err)
481469
}
@@ -494,79 +482,80 @@ func (m *ManagerClient) PostLogs(r io.Reader) error {
494482
return fmt.Errorf("PostLogs failed, server cannot be authenticated")
495483
}
496484

497-
// ExecuteCommand executes a Command on the endpoint and return the result
498-
// to the manager. NB: this method is blocking due to Command.Run function call
499-
func (m *ManagerClient) ExecuteCommand() error {
485+
var (
486+
ErrNothingToDo = fmt.Errorf("nothing to do")
487+
)
488+
489+
func (m *ManagerClient) PostCommand(command *Command) error {
500490
if auth, _ := m.IsServerAuthenticated(); auth {
501-
env := AliasEnv{m.managerIP}
502-
command := NewCommandWithEnv(&env)
491+
// stripping unecessary content to send back the command
492+
command.Strip()
503493

504-
// getting command to be executed
505-
req, err := m.Prepare("GET", EptAPICommandPath, nil)
494+
// command should now contain stdout and stderr
495+
jsonCommand, err := json.Marshal(command)
506496
if err != nil {
507-
return fmt.Errorf("ExecuteCommand failed to prepare request: %s", err)
497+
return fmt.Errorf("PostCommand failed to marshal command")
508498
}
509499

510-
resp, err := m.httpClient.Do(req)
500+
// send back the response
501+
req, err := m.PrepareGzip("POST", EptAPICommandPath, bytes.NewBuffer(jsonCommand))
511502
if err != nil {
512-
return fmt.Errorf("ExecuteCommand failed to issue HTTP request: %s", err)
503+
return fmt.Errorf("PostCommand failed to prepare POST request")
513504
}
514505

515-
// if there is no command to execute, the server replies with this status code
516-
if resp.StatusCode == http.StatusNoContent {
517-
// nothing else to do
518-
return nil
519-
}
520-
521-
jsonCommand, err := ioutil.ReadAll(resp.Body)
506+
resp, err := m.HTTPClient.Do(req)
522507
if err != nil {
523-
return fmt.Errorf("ExecuteCommand failed to read HTTP response body: %s", err)
508+
return fmt.Errorf("PostCommand failed to issue HTTP request: %s", err)
524509
}
525510

526-
// unmarshal command to be executed
527-
if err := json.Unmarshal(jsonCommand, &command); err != nil {
528-
return fmt.Errorf("ExecuteCommand failed to unmarshal command: %s", err)
511+
if resp != nil {
512+
defer resp.Body.Close()
513+
if resp.StatusCode != 200 {
514+
return fmt.Errorf("PostCommand failed to send command results, unexpected HTTP status code %d", resp.StatusCode)
515+
}
529516
}
517+
return nil
518+
}
519+
return fmt.Errorf("PostCommand failed, server cannot be authenticated")
530520

531-
// running the command, this is a blocking function, it waits the command to finish
532-
if err := command.Run(); err != nil {
533-
log.Errorf("ExecuteCommand failed to run command \"%s\": %s", command, err)
534-
}
521+
}
535522

536-
// stripping unecessary content to send back the command
537-
command.Strip()
538-
for fn, ff := range command.Fetch {
539-
log.Infof("file: %s len: %d error: %s", fn, len(ff.Data), ff.Error)
540-
}
541-
// command should now contain stdout and stderr
542-
jsonCommand, err = json.Marshal(command)
523+
func (m *ManagerClient) FetchCommand() (*Command, error) {
524+
command := NewCommand()
525+
if auth, _ := m.IsServerAuthenticated(); auth {
526+
// getting command to be executed
527+
req, err := m.Prepare("GET", EptAPICommandPath, nil)
543528
if err != nil {
544-
return fmt.Errorf("ExecuteCommand failed to marshal command")
529+
return command, fmt.Errorf("FetchCommand failed to prepare request: %s", err)
545530
}
546531

547-
// send back the response
548-
req, err = m.PrepareGzip("POST", EptAPICommandPath, bytes.NewBuffer(jsonCommand))
532+
resp, err := m.HTTPClient.Do(req)
549533
if err != nil {
550-
return fmt.Errorf("ExecuteCommand failed to prepare POST request")
534+
return command, fmt.Errorf("FetchCommand failed to issue HTTP request: %s", err)
551535
}
552536

553-
resp, err = m.httpClient.Do(req)
537+
// if there is no command to execute, the server replies with this status code
538+
if resp.StatusCode == http.StatusNoContent {
539+
// nothing else to do
540+
return command, ErrNothingToDo
541+
}
542+
543+
jsonCommand, err := ioutil.ReadAll(resp.Body)
554544
if err != nil {
555-
return fmt.Errorf("ExecuteCommand failed to issue HTTP request: %s", err)
545+
return command, fmt.Errorf("FetchCommand failed to read HTTP response body: %s", err)
556546
}
557547

558-
if resp != nil {
559-
defer resp.Body.Close()
560-
if resp.StatusCode != 200 {
561-
return fmt.Errorf("ExecuteCommand failed to send command results, unexpected HTTP status code %d", resp.StatusCode)
562-
}
548+
// unmarshal command to be executed
549+
if err := json.Unmarshal(jsonCommand, &command); err != nil {
550+
return command, fmt.Errorf("FetchCommand failed to unmarshal command: %s", err)
563551
}
564-
return nil
552+
553+
return command, nil
565554
}
566-
return fmt.Errorf("ExecuteCommand failed, server cannot be authenticated")
555+
return command, fmt.Errorf("FetchCommand failed, server cannot be authenticated")
567556
}
568557

569558
// Close closes idle connections from underlying transport
570559
func (m *ManagerClient) Close() {
571-
m.httpClient.CloseIdleConnections()
560+
m.HTTPClient.CloseIdleConnections()
572561
}

0 commit comments

Comments
 (0)