diff --git a/src/mig/agent/agent.go b/src/mig/agent/agent.go index 530912fb..6bdb495a 100644 --- a/src/mig/agent/agent.go +++ b/src/mig/agent/agent.go @@ -498,8 +498,8 @@ func runModule(ctx Context, op moduleOp) (err error) { execTimeOut = op.expireafter.Sub(time.Now()) } - // Command arguments must be in json format - modParams, err := json.Marshal(op.params) + // Build parameters message + modParams, err := json.Marshal(modules.MakeParametersMessage(op.params)) if err != nil { panic(err) } diff --git a/src/mig/api/compliance.go b/src/mig/api/compliance.go index 74c65e07..9d9f002d 100644 --- a/src/mig/api/compliance.go +++ b/src/mig/api/compliance.go @@ -9,6 +9,7 @@ import ( "encoding/json" "fmt" "mig" + "mig/modules" "mig/modules/file" "time" ) @@ -73,12 +74,17 @@ func commandsToComplianceItems(commands []mig.Command) (items []ComplianceItem, } switch cmd.Action.Operations[i].Module { case "file": - var r file.Results + var el file.SearchResults + var r modules.Result err = json.Unmarshal(buf, &r) if err != nil { return items, err } - for label, sr := range r.Elements { + err = r.GetElements(&el) + if err != nil { + return items, err + } + for label, sr := range el { for _, mf := range sr { bitem.Check.Location = mf.File bitem.Check.Name = label diff --git a/src/mig/client/client.go b/src/mig/client/client.go index 30a06b33..52d08ec7 100644 --- a/src/mig/client/client.go +++ b/src/mig/client/client.go @@ -18,6 +18,7 @@ import ( "io" "io/ioutil" "mig" + "mig/modules" "mig/pgp" "mime/multipart" "net/http" @@ -847,7 +848,7 @@ func PrintCommandResults(cmd mig.Command, onlyFound, showAgent bool) (err error) } for i, result := range cmd.Results { if !onlyFound { - for _, rerr := range cmd.Results.Errors { + for _, rerr := range result.Errors { fmt.Fprintf(os.Stderr, "%s[error] %s\n", prefix, rerr) } } @@ -865,10 +866,10 @@ func PrintCommandResults(cmd mig.Command, onlyFound, showAgent bool) (err error) } continue } - modRunner := mig.AvailableModules[moduleName].Runner() + modRunner := modules.Available[moduleName].Runner() // look for a result printer in the module - if _, ok := modRunner.(mig.HasResultsPrinter); ok { - outRes, err := modRunner.(mig.HasResultsPrinter).PrintResults(result, onlyFound) + if _, ok := modRunner.(modules.HasResultsPrinter); ok { + outRes, err := modRunner.(modules.HasResultsPrinter).PrintResults(result, onlyFound) if err != nil { panic(err) } diff --git a/src/mig/client/cmd/main.go b/src/mig/client/cmd/main.go index 80aa4d2a..97f4d8ea 100644 --- a/src/mig/client/cmd/main.go +++ b/src/mig/client/cmd/main.go @@ -10,6 +10,7 @@ import ( "fmt" "mig" "mig/client" + "mig/modules" "os" "os/signal" "time" @@ -50,7 +51,7 @@ Results are sent to stdout, redirect them with "1>/path/to/file". Each module provides its own set of parameters. Module parameters must be set *after* global options. Help is available by calling " help". Available modules are: `, os.Args[0], os.Args[0]) - for module, _ := range mig.AvailableModules { + for module, _ := range modules.Available { fmt.Printf("* %s\n", module) } fmt.Printf("To access a module documentation, use: %s help\n", os.Args[0]) @@ -125,7 +126,7 @@ func main() { // * fs.Args() with the module parameters is passed as a string to the module parser // which will return a module operation to store in the action op.Module = os.Args[1] - if _, ok := mig.AvailableModules[op.Module]; !ok { + if _, ok := modules.Available[op.Module]; !ok { panic("Unknown module " + op.Module) } @@ -154,12 +155,12 @@ func main() { for _, arg := range fs.Args() { modargs = append(modargs, arg) } - modRunner = mig.AvailableModules[op.Module].Runner() - if _, ok := modRunner.(mig.HasParamsParser); !ok { + modRunner = modules.Available[op.Module].Runner() + if _, ok := modRunner.(modules.HasParamsParser); !ok { fmt.Fprintf(os.Stderr, "[error] module '%s' does not support command line invocation\n", op.Module) os.Exit(2) } - op.Parameters, err = modRunner.(mig.HasParamsParser).ParamsParser(modargs) + op.Parameters, err = modRunner.(modules.HasParamsParser).ParamsParser(modargs) if err != nil || op.Parameters == nil { panic(err) } diff --git a/src/mig/client/console/action_launcher.go b/src/mig/client/console/action_launcher.go index e292b7ab..293617d1 100644 --- a/src/mig/client/console/action_launcher.go +++ b/src/mig/client/console/action_launcher.go @@ -12,6 +12,7 @@ import ( "io" "mig" "mig/client" + "mig/modules" "strconv" "strings" "time" @@ -80,15 +81,15 @@ func actionLauncher(tpl mig.Action, cli client.Client) (err error) { // ParamsCreator takes care of retrieving using input var operation mig.Operation operation.Module = orders[1] - if _, ok := mig.AvailableModules[operation.Module]; ok { + if _, ok := modules.Available[operation.Module]; ok { // instanciate and call module parameters creation function - modRunner := mig.AvailableModules[operation.Module].Runner() - if _, ok := modRunner.(mig.HasParamsCreator); !ok { + modRunner := modules.Available[operation.Module].Runner() + if _, ok := modRunner.(modules.HasParamsCreator); !ok { fmt.Println(operation.Module, "module does not provide a parameters creator.") fmt.Println("You can write your action by hand and import it using 'load '") break } - operation.Parameters, err = modRunner.(mig.HasParamsCreator).ParamsCreator() + operation.Parameters, err = modRunner.(modules.HasParamsCreator).ParamsCreator() if err != nil { fmt.Printf("Parameters creation failed with error: %v\n", err) break diff --git a/src/mig/modules/file/file.go b/src/mig/modules/file/file.go index 93f0e101..f9c130be 100644 --- a/src/mig/modules/file/file.go +++ b/src/mig/modules/file/file.go @@ -22,7 +22,7 @@ import ( "fmt" "hash" "io" - "mig" + "mig/modules" "os" "path" "path/filepath" @@ -36,14 +36,14 @@ import ( var debug bool = false func init() { - mig.RegisterModule("file", func() interface{} { + modules.Register("file", func() interface{} { return new(Runner) - }, false) + }) } type Runner struct { Parameters Parameters - Results Results + Results modules.Result } type Parameters struct { @@ -618,7 +618,7 @@ var stats statistics var walkingErrors []string -func (r Runner) Run(Args []byte) (resStr string) { +func (r Runner) Run() (resStr string) { var ( roots []string traversed []string @@ -639,7 +639,7 @@ func (r Runner) Run(Args []byte) (resStr string) { } }() t0 := time.Now() - err := json.Unmarshal(Args, &r.Parameters) + err := modules.ReadInputParameters(&r.Parameters) if err != nil { panic(err) } @@ -720,7 +720,9 @@ func (r Runner) Run(Args []byte) (resStr string) { if debug { fmt.Println("---- results ----") - printedResults, err := r.PrintResults([]byte(resStr), false) + var tmpres modules.Result + err = json.Unmarshal([]byte(resStr), &tmpres) + printedResults, err := r.PrintResults(tmpres, false) if err != nil { panic(err) } @@ -1292,15 +1294,7 @@ func getHash(file string, hashType checkType) (hexhash string, err error) { return } -type Results struct { - FoundAnything bool `json:"foundanything"` - Success bool `json:"success"` - Elements searchresults `json:"elements"` - Statistics statistics `json:"statistics"` - Errors []string `json:"error"` -} - -type searchresults map[string]searchresult +type SearchResults map[string]searchresult type searchresult []matchedfile @@ -1317,8 +1311,8 @@ type fileinfo struct { } // newResults allocates a Results structure -func newResults() *Results { - return &Results{Elements: make(searchresults), FoundAnything: false} +func newResults() *modules.Result { + return &modules.Result{Elements: make(SearchResults), FoundAnything: false} } func (r Runner) buildResults(t0 time.Time) (resStr string, err error) { @@ -1328,6 +1322,7 @@ func (r Runner) buildResults(t0 time.Time) (resStr string, err error) { } }() res := newResults() + elements := res.Elements.(SearchResults) for label, search := range r.Parameters.Searches { var sr searchresult // first pass on the results: if matchall is set, verify that all @@ -1462,7 +1457,7 @@ func (r Runner) buildResults(t0 time.Time) (resStr string, err error) { } } nextsearch: - res.Elements[label] = sr + elements[label] = sr } // calculate execution time @@ -1501,13 +1496,21 @@ func (r Runner) buildResults(t0 time.Time) (resStr string, err error) { // only results that have at least one match are returned. // If foundOnly is not set, all results are returned, along with errors and // statistics. -func (r Runner) PrintResults(rawResults []byte, foundOnly bool) (prints []string, err error) { - var results Results - err = json.Unmarshal(rawResults, &results) +func (r Runner) PrintResults(result modules.Result, foundOnly bool) (prints []string, err error) { + var ( + el SearchResults + stats statistics + ) + err = result.GetElements(&el) if err != nil { panic(err) } - for label, sr := range results.Elements { + err = result.GetStatistics(&stats) + if err != nil { + panic(err) + } + + for label, sr := range el { for _, mf := range sr { var out string if mf.File == "" { @@ -1571,12 +1574,12 @@ func (r Runner) PrintResults(rawResults []byte, foundOnly bool) (prints []string } } if !foundOnly { - for _, we := range results.Errors { + for _, we := range result.Errors { prints = append(prints, we) } stat := fmt.Sprintf("Statistics: %.0f files checked, %.0f failed to open, %.0f matched, ran in %s.", - results.Statistics.Filescount, results.Statistics.Openfailed, - results.Statistics.Totalhits, results.Statistics.Exectime) + stats.Filescount, stats.Openfailed, + stats.Totalhits, stats.Exectime) prints = append(prints, stat) } return diff --git a/src/mig/modules/modules.go b/src/mig/modules/modules.go index 69c38c79..4b2dba09 100644 --- a/src/mig/modules/modules.go +++ b/src/mig/modules/modules.go @@ -15,8 +15,8 @@ import ( // Message defines the input messages received by modules. type Message struct { - Class string // represent the type of message being passed to the module - Parameters interface{} // for `parameters` class, this interface contains the module parameters + Class string `json:"class"` // represent the type of message being passed to the module + Parameters interface{} `json:"parameters"` // for `parameters` class, this interface contains the module parameters } const ( @@ -74,6 +74,12 @@ type Moduler interface { ValidateParameters() error } +func MakeParametersMessage(params interface{}) (ret Message) { + ret.Class = MsgClassParameters + ret.Parameters = params + return +} + // ReadInput reads one line of input from stdin, unmarshal it into a modules.Message // and returns the message to the caller func ReadInput() (msg Message, err error) { diff --git a/src/mig/scheduler/scheduler.go b/src/mig/scheduler/scheduler.go index caadd1e0..dfe0f973 100644 --- a/src/mig/scheduler/scheduler.go +++ b/src/mig/scheduler/scheduler.go @@ -11,6 +11,7 @@ import ( "fmt" "github.com/streadway/amqp" "mig" + "mig/modules" "mig/pgp" "os" "runtime" @@ -133,7 +134,7 @@ func processNewAction(actionPath string, ctx Context) (err error) { ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, ActionID: action.ID, Desc: "Action written to database"}.Debug() // create an array of empty results to serve as default for all commands - emptyResults := make([]mig.ModuleResult, len(action.Operations)) + emptyResults := make([]modules.Result, len(action.Operations)) created := 0 for _, agent := range agents { err := createCommand(ctx, action, agent, emptyResults) @@ -161,7 +162,7 @@ func processNewAction(actionPath string, ctx Context) (err error) { return } -func createCommand(ctx Context, action mig.Action, agent mig.Agent, emptyResults []mig.ModuleResult) (err error) { +func createCommand(ctx Context, action mig.Action, agent mig.Agent, emptyResults []modules.Result) (err error) { cmdid := mig.GenID() defer func() { if e := recover(); e != nil { diff --git a/src/mig/workers/compliance_item/main.go b/src/mig/workers/compliance_item/main.go index f3d0d67f..c1f8ccff 100644 --- a/src/mig/workers/compliance_item/main.go +++ b/src/mig/workers/compliance_item/main.go @@ -13,6 +13,7 @@ import ( "github.com/jvehent/gozdef" "mig" "mig/event" + "mig/modules" "mig/modules/file" "mig/workers" "os" @@ -141,12 +142,17 @@ func makeComplianceItem(cmd mig.Command, conf Config) (items []gozdef.Compliance } switch cmd.Action.Operations[i].Module { case "file": - var r file.Results + var r modules.Result + var el file.SearchResults err = json.Unmarshal(buf, &r) if err != nil { return items, err } - for label, sr := range r.Elements { + err = r.GetElements(&el) + if err != nil { + return items, err + } + for label, sr := range el { for _, mf := range sr { ci.Check.Location = mf.File ci.Check.Name = label