func (r *run) Run(in io.Reader) (resStr string) { defer func() { if e := recover(); e != nil { // return error in json r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", e)) r.Results.Success = false endCounters() r.Results.Statistics = stats err, _ := json.Marshal(r.Results) resStr = string(err) return } }() // Restrict go runtime processor utilization here, this might be moved // into a more generic agent module function at some point. runtime.GOMAXPROCS(1) startCounters() // Read module parameters from stdin err := modules.ReadInputParameters(in, &r.Parameters) if err != nil { panic(err) } err = r.ValidateParameters() if err != nil { panic(err) } e := &elements{} e.Packages = make([]scribelib.PackageInfo, 0) pkglist := scribelib.QueryPackages() for _, x := range r.Parameters.PkgMatch.Matches { re, err := regexp.Compile(x) if err != nil { panic(err) } for _, y := range pkglist { if !re.MatchString(y.Name) { continue } e.Packages = append(e.Packages, y) } } buf, err := buildResults(*e, &r.Results) if err != nil { panic(err) } resStr = string(buf) return }
// Run *must* be implemented by a module. Its the function that executes the module. // It must return a string of marshalled json that contains the results from the module. // The code below provides a base module skeleton that can be reused in all modules. func (r Runner) Run(in io.Reader) (out string) { // a good way to handle execution failures is to catch panics and store // the panicked error into modules.Results.Errors, marshal that, and output // the JSON string back to the caller defer func() { if e := recover(); e != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", e)) r.Results.Success = false buf, _ := json.Marshal(r.Results) out = string(buf[:]) } }() // read module parameters from stdin err := modules.ReadInputParameters(in, &r.Parameters) if err != nil { panic(err) } // verify that the parameters we received are valid err = r.ValidateParameters() if err != nil { panic(err) } // start a goroutine that does some work and another one that looks // for an early stop signal moduleDone := make(chan bool) stop := make(chan bool) go r.doModuleStuff(&out, &moduleDone) go modules.WatchForStop(in, &stop) select { case <-moduleDone: return out case <-stop: panic("stop message received, terminating early") } }
func (r Runner) Run(in io.Reader) (out string) { var ( err error el elements ) defer func() { if e := recover(); e != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", e)) r.Results.Success = false buf, _ := json.Marshal(r.Results) out = string(buf[:]) } }() err = modules.ReadInputParameters(in, &r.Parameters) if err != nil { panic(err) } err = r.ValidateParameters() if err != nil { panic(err) } el.ResolvedHost = r.Parameters.Destination if r.Parameters.Protocol == "udp" || r.Parameters.Protocol == "tcp" { el.ResolvedHost += fmt.Sprintf(":%.0f", r.Parameters.DestinationPort) } el.ResolvedHost += " (" + r.Parameters.ipDest + ")" el.Protocol = r.Parameters.Protocol for i := 0; i < int(r.Parameters.Count); i += 1 { var err error // startTime for calculating the latency/RTT startTime := time.Now() switch r.Parameters.Protocol { case "icmp": err = r.pingIcmp() case "tcp": err = r.pingTcp() case "udp": err = r.pingUdp() } // store the time elapsed before processing potential errors latency := time.Since(startTime).Seconds() * 1000 // evaluate potential ping failures if err != nil { switch err.Error() { case E_Timeout: latency = 9999999 case E_ConnRefused: latency = -1 default: el.Failures = append(el.Failures, fmt.Sprintf("ping #%d failed with error: %v", i+1, err)) latency = 0 } } switch latency { case -1, 0: // do nothing case 9999999: // For udp, a timeout indicates that the port *maybe* open. if r.Parameters.Protocol == "udp" { r.Results.FoundAnything = true } default: r.Results.FoundAnything = true } el.Latencies = append(el.Latencies, latency) // sleep 100 milliseconds between pings to prevent floods time.Sleep(100 * time.Millisecond) } return r.buildResults(el) }
func (r *run) Run(in io.Reader) (resStr string) { defer func() { if e := recover(); e != nil { // return error in json r.Results.Success = false r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", e)) resJson, _ := json.Marshal(r.Results) resStr = string(resJson[:]) return } }() t0 := time.Now() // read module parameters from stdin err := modules.ReadInputParameters(in, &r.Parameters) if err != nil { panic(err) } err = r.ValidateParameters() if err != nil { panic(err) } els := *newElements() for _, val := range r.Parameters.LocalMAC { found, el, err := HasLocalMAC(val) if err != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", err)) } els.LocalMAC[val] = el if found { r.Results.FoundAnything = true } } for _, val := range r.Parameters.NeighborMAC { found, el, err := HasSeenMac(val) if err != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", err)) } els.NeighborMAC[val] = el if found { r.Results.FoundAnything = true } } for _, val := range r.Parameters.LocalIP { found, el, err := HasLocalIP(val) if err != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", err)) } els.LocalIP[val] = el if found { r.Results.FoundAnything = true } } for _, val := range r.Parameters.ConnectedIP { found, el, err := HasIPConnected(val) if err != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", err)) } els.ConnectedIP[val] = el if found { r.Results.FoundAnything = true } } for _, port := range r.Parameters.ListeningPort { found, el, err := HasListeningPort(port) if err != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", err)) } els.ListeningPort[port] = el if found { r.Results.FoundAnything = true } } r.Results.Elements = els // calculate execution time t1 := time.Now() stats.Exectime = t1.Sub(t0).String() r.Results.Statistics = stats r.Results.Success = true jsonOutput, err := json.Marshal(r.Results) if err != nil { panic(err) } resStr = string(jsonOutput[:]) return }
func (r Runner) Run(in io.Reader) (out string) { defer func() { if e := recover(); e != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", e)) r.Results.Success = false buf, _ := json.Marshal(r.Results) out = string(buf[:]) } }() // read module parameters from stdin err := modules.ReadInputParameters(in, &r.Parameters) if err != nil { panic(err) } // verify that the parameters we received are valid err = r.ValidateParameters() if err != nil { panic(err) } // Refuse to suicide if r.Parameters.PID == os.Getppid() { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("PID '%d' is mine. Refusing to suicide.", r.Parameters.PID)) return r.buildResults() } // get the path of the agent's executable var targetExecutable string switch runtime.GOOS { case "linux", "darwin", "freebsd", "openbsd", "netbsd": targetExecutable, err = os.Readlink(fmt.Sprintf("/proc/%d/exe", r.Parameters.PID)) if err != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("Executable path of PID '%d' not found: '%v'", r.Parameters.PID, err)) return r.buildResults() } case "windows": targetExecutable = fmt.Sprintf("C:\\Program Files\\mig\\mig-agent-%s.exe", r.Parameters.Version) default: r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("'%s' isn't a supported OS", runtime.GOOS)) return r.buildResults() } // verify that the executable we're removing isn't in use by the current agent // this can happen when two agents are running of the same executable // in which case, do not remove the executable, and only kill the process myExecutable, err := osext.Executable() if err != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("Failed to retrieve my executable location: '%v'", err)) return r.buildResults() } removeExecutable := true if myExecutable == targetExecutable { r.Results.Errors = append(r.Results.Errors, "Executable not removed because current agent uses it as well") removeExecutable = false } if removeExecutable { // check that the binary we're removing has the right version version, err := getAgentVersion(targetExecutable) if err != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("Failed to check agent version: '%v'", err)) return r.buildResults() } if version != r.Parameters.Version { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("Version mismatch. Expected '%s', found '%s'", r.Parameters.Version, version)) return r.buildResults() } err = os.Remove(targetExecutable) if err != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("Failed to remove executable '%s': '%v'", targetExecutable, err)) return r.buildResults() } } // Then kill the PID process, err := os.FindProcess(r.Parameters.PID) if err != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("PID '%d' not found. Returned error '%v'", r.Parameters.PID, err)) return r.buildResults() } else { err = process.Kill() if err != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("PID '%d' not killed. Returned error '%v'", r.Parameters.PID, err)) return r.buildResults() } } r.Results.Success = true return r.buildResults() }
func (r Runner) Run(in io.Reader) (out string) { var ts statistics stats = ts // in debug mode, we just panic if !debug { defer func() { if e := recover(); e != nil { // return error in json res := newResults() res.Statistics = stats res.Errors = append(res.Errors, fmt.Sprintf("%v", e)) res.Success = false err, _ := json.Marshal(res) out = string(err[:]) return } }() } t0 := time.Now() err := modules.ReadInputParameters(in, &r.Parameters) if err != nil { panic(err) } err = r.ValidateParameters() if err != nil { panic(err) } // create the checks based on the search parameters for label, search := range r.Parameters.Searches { if debug { fmt.Println("making checks for label", label) } err := search.makeChecks() if err != nil { panic(err) } r.Parameters.Searches[label] = search } // evaluate each process one by one pids, err, serr := process.GetAllPids() if err != nil { panic(err) } if debug { fmt.Println("found", len(pids), "processes to evaluate") } for _, err = range serr { stats.Failures = append(stats.Failures, err.Error()) } for _, pid := range pids { // activate all searches for label, search := range r.Parameters.Searches { search.activate() r.Parameters.Searches[label] = search } proc, err, serr := process.OpenFromPid(pid) if err != nil { // if we encounter a hard failure, skip this process stats.Failures = append(stats.Failures, err.Error()) continue } for _, err = range serr { // soft failures are just logged but we continue inspection stats.Failures = append(stats.Failures, err.Error()) } err = r.evaluateProcess(proc) if err != nil { stats.Failures = append(stats.Failures, err.Error()) } stats.ProcessCount++ } out, err = r.buildResults(t0) if err != nil { panic(err) } if debug { fmt.Println("---- results ----") var tmpres modules.Result err = json.Unmarshal([]byte(out), &tmpres) printedResults, err := r.PrintResults(tmpres, false) if err != nil { panic(err) } for _, res := range printedResults { fmt.Println(res) } } return }
func (r *run) Run(in io.Reader) (resStr string) { defer func() { if e := recover(); e != nil { // return error in json r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", e)) r.Results.Success = false endCounters() r.Results.Statistics = stats err, _ := json.Marshal(r.Results) resStr = string(err) return } }() // Restrict go runtime processor utilization here, this might be moved // into a more generic agent module function at some point. runtime.GOMAXPROCS(1) startCounters() // Read module parameters from stdin err := modules.ReadInputParameters(in, &r.Parameters) if err != nil { panic(err) } err = r.ValidateParameters() if err != nil { panic(err) } ovallib.SetMaxChecks(r.Parameters.MaxConcurrentEval) e := &elements{} if len(r.Parameters.PkgMatch.Matches) > 0 { oresp := ovallib.PackageQuery(r.Parameters.PkgMatch.Matches, true) for _, x := range oresp { npi := &PkgInfo{PkgName: x.Name, PkgVersion: x.Version, PkgType: x.PkgType} e.Matches = append(e.Matches, *npi) } buf, err := buildResults(*e, &r.Results, len(e.Matches)) if err != nil { panic(err) } resStr = string(buf) return } else if r.Parameters.OvalDef != "" { stats.InDefSize = len(r.Parameters.OvalDef) ovalbuf, err := makeOvalString(r.Parameters.OvalDef) if err != nil { panic(err) } pst := time.Now() od, err := ovallib.ParseBuffer(ovalbuf) stats.Parsetime = time.Now().Sub(pst).String() if err != nil { panic(err) } ovalresults, err := ovallib.Execute(od) if err != nil { panic(err) } for _, x := range ovalresults { if !r.Parameters.IncludeFalse { if x.Status == ovallib.RESULT_FALSE { continue } } nmor := &MOResult{} nmor.Title = x.Title nmor.Status = x.StatusString() nmor.ID = x.ID e.OvalResults = append(e.OvalResults, *nmor) } buf, err := buildResults(*e, &r.Results, len(e.OvalResults)) if err != nil { panic(err) } resStr = string(buf) return } panic("no function specified") return }
func (r Runner) Run(in io.Reader) (out string) { var ( stats statistics el elements drift time.Duration ) defer func() { if e := recover(); e != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", e)) r.Results.Success = false buf, _ := json.Marshal(r.Results) out = string(buf[:]) } }() el.LocalTime = time.Now().Format(time.RFC3339Nano) t1 := time.Now() err := modules.ReadInputParameters(in, &r.Parameters) if err != nil { panic(err) } err = r.ValidateParameters() if err != nil { panic(err) } // if drift is not set, skip the ntp test if r.Parameters.Drift == "" { r.Results.FoundAnything = true goto done } drift, err = time.ParseDuration(r.Parameters.Drift) if err != nil { panic(err) } // assume host has synched time and set to false if not true el.IsWithinDrift = true // attempt to get network time from each of the NTP servers, and exit // as soon as we get a valid result from one of them for i := 0; i < len(NtpPool); i++ { // pick a server between 0 and len of ntppool, somewhat randomly ntpsrv := NtpPool[time.Now().Nanosecond()%len(NtpPool)] t, lat, err := GetNetworkTime(ntpsrv) if err != nil { // failed to get network time, log a failure and try another one stats.NtpStats = append(stats.NtpStats, ntpstats{ Host: ntpsrv, Reachable: false, }) continue } // compare network time to local time localtime := time.Now() if err != nil { r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", err)) continue } if localtime.Before(t.Add(-drift)) { el.IsWithinDrift = false el.Drifts = append(el.Drifts, fmt.Sprintf("Local time is behind ntp host %s by %s", ntpsrv, t.Sub(localtime).String())) } else if localtime.After(t.Add(drift)) { el.IsWithinDrift = false el.Drifts = append(el.Drifts, fmt.Sprintf("Local time is ahead of ntp host %s by %s", ntpsrv, localtime.Sub(t).String())) } stats.NtpStats = append(stats.NtpStats, ntpstats{ Host: ntpsrv, Time: t, Latency: lat, Drift: localtime.Sub(t).String(), Reachable: true, }) el.HasCheckedDrift = true // comparison succeeded, exit the loop break } if !el.IsWithinDrift { r.Results.FoundAnything = true } done: stats.ExecTime = time.Now().Sub(t1).String() out = r.buildResults(el, stats) return }