package tactical import ( "errors" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "time" "github.com/amidaware/rmmagent/agent/patching" "github.com/amidaware/rmmagent/agent/services" "github.com/amidaware/rmmagent/agent/software" "github.com/amidaware/rmmagent/agent/system" "github.com/amidaware/rmmagent/agent/tactical/rpc" "github.com/amidaware/rmmagent/agent/tasks" "github.com/amidaware/rmmagent/agent/utils" rmm "github.com/amidaware/rmmagent/shared" "github.com/go-resty/resty/v2" "github.com/kardianos/service" trmm "github.com/wh1te909/trmm-shared" "golang.org/x/sys/windows" "golang.org/x/sys/windows/registry" ) func GetMeshBinary() string { var MeshSysBin string ac := NewAgentConfig() if len(ac.CustomMeshDir) > 0 { MeshSysBin = filepath.Join(ac.CustomMeshDir, "MeshAgent.exe") } else { MeshSysBin = filepath.Join(os.Getenv("ProgramFiles"), "Mesh Agent", "MeshAgent.exe") } return MeshSysBin } func NewAgentConfig() *rmm.AgentConfig { k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`, registry.ALL_ACCESS) if err != nil { return &rmm.AgentConfig{} } baseurl, _, _ := k.GetStringValue("BaseURL") agentid, _, _ := k.GetStringValue("AgentID") apiurl, _, _ := k.GetStringValue("ApiURL") token, _, _ := k.GetStringValue("Token") agentpk, _, _ := k.GetStringValue("AgentPK") pk, _ := strconv.Atoi(agentpk) cert, _, _ := k.GetStringValue("Cert") proxy, _, _ := k.GetStringValue("Proxy") customMeshDir, _, _ := k.GetStringValue("MeshDir") return &rmm.AgentConfig{ BaseURL: baseurl, AgentID: agentid, APIURL: apiurl, Token: token, AgentPK: agentpk, PK: pk, Cert: cert, Proxy: proxy, CustomMeshDir: customMeshDir, } } func GetMeshNodeID() (string, error) { out, err := system.CMD(GetMeshBinary(), []string{"-nodeid"}, 10, false) if err != nil { //a.Logger.Debugln(err) return "", err } stdout := out[0] stderr := out[1] if stderr != "" { //a.Logger.Debugln(stderr) return "", err } if stdout == "" || strings.Contains(strings.ToLower(utils.StripAll(stdout)), "not defined") { //a.Logger.Debugln("Failed getting mesh node id", stdout) return "", errors.New("failed to get mesh node id") } return stdout, nil } func SendSoftware() { sw := software.GetInstalledSoftware() //a.Logger.Debugln(sw) config := NewAgentConfig() payload := map[string]interface{}{ "agent_id": config.AgentID, "software": sw, } _, err := PostRequest("/api/v3/software/", payload, 15) if err != nil { //a.Logger.Debugln(err) } } func UninstallCleanup() { registry.DeleteKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`) patching.PatchMgmnt(false) CleanupAgentUpdates() tasks.CleanupSchedTasks() } func AgentUpdate(url, inno, version string) { time.Sleep(time.Duration(utils.RandRange(1, 15)) * time.Second) system.KillHungUpdates() CleanupAgentUpdates() updater := filepath.Join(system.GetProgramDirectory(), inno) //a.Logger.Infof("Agent updating from %s to %s", a.Version, version) //a.Logger.Infoln("Downloading agent update from", url) config := NewAgentConfig() rClient := resty.New() rClient.SetCloseConnection(true) rClient.SetTimeout(15 * time.Minute) rClient.SetDebug(rmm.DEBUG) if len(config.Proxy) > 0 { rClient.SetProxy(config.Proxy) } r, err := rClient.R().SetOutput(updater).Get(url) if err != nil { //a.Logger.Errorln(err) system.CMD("net", []string{"start", services.WinSvcName}, 10, false) return } if r.IsError() { //a.Logger.Errorln("Download failed with status code", r.StatusCode()) system.CMD("net", []string{"start", services.WinSvcName}, 10, false) return } dir, err := ioutil.TempDir("", "tacticalrmm") if err != nil { //a.Logger.Errorln("Agentupdate create tempdir:", err) system.CMD("net", []string{"start", services.WinSvcName}, 10, false) return } innoLogFile := filepath.Join(dir, "tacticalrmm.txt") args := []string{"/C", updater, "/VERYSILENT", fmt.Sprintf("/LOG=%s", innoLogFile)} cmd := exec.Command("cmd.exe", args...) cmd.SysProcAttr = &windows.SysProcAttr{ CreationFlags: windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP, } cmd.Start() time.Sleep(1 * time.Second) } func CleanupAgentUpdates() { pd := filepath.Join(os.Getenv("ProgramFiles"), system.ProgFilesName) cderr := os.Chdir(pd) if cderr != nil { //a.Logger.Errorln(cderr) return } files, err := filepath.Glob("winagent-v*.exe") if err == nil { for _, f := range files { os.Remove(f) } } cderr = os.Chdir(os.Getenv("TMP")) if cderr != nil { //a.Logger.Errorln(cderr) return } folders, err := filepath.Glob("tacticalrmm*") if err == nil { for _, f := range folders { os.RemoveAll(f) } } } func AgentUninstall(code string) { system.KillHungUpdates() tacUninst := filepath.Join(system.GetProgramDirectory(), GetUninstallExe()) args := []string{"/C", tacUninst, "/VERYSILENT"} cmd := exec.Command("cmd.exe", args...) cmd.SysProcAttr = &windows.SysProcAttr{ CreationFlags: windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP, } cmd.Start() } func GetUninstallExe() string { cderr := os.Chdir(system.GetProgramDirectory()) if cderr == nil { files, err := filepath.Glob("unins*.exe") if err == nil { for _, f := range files { if strings.Contains(f, "001") { return f } } } } return "unins000.exe" } // RunMigrations cleans up unused stuff from older agents func RunMigrations() { for _, i := range []string{"nssm.exe", "nssm-x86.exe"} { nssm := filepath.Join(system.GetProgramDirectory(), i) if trmm.FileExists(nssm) { os.Remove(nssm) } } } func installMesh(meshbin, exe, proxy string) (string, error) { var meshNodeID string meshInstallArgs := []string{"-fullinstall"} if len(proxy) > 0 { meshProxy := fmt.Sprintf("--WebProxy=%s", proxy) meshInstallArgs = append(meshInstallArgs, meshProxy) } //a.Logger.Debugln("Mesh install args:", meshInstallArgs) meshOut, meshErr := system.CMD(meshbin, meshInstallArgs, int(90), false) if meshErr != nil { fmt.Println(meshOut[0]) fmt.Println(meshOut[1]) fmt.Println(meshErr) } fmt.Println(meshOut) //a.Logger.Debugln("Sleeping for 5") time.Sleep(5 * time.Second) meshSuccess := false for !meshSuccess { //a.Logger.Debugln("Getting mesh node id") pMesh, pErr := system.CMD(exe, []string{"-nodeid"}, int(30), false) if pErr != nil { //a.Logger.Errorln(pErr) time.Sleep(5 * time.Second) continue } if pMesh[1] != "" { //a.Logger.Errorln(pMesh[1]) time.Sleep(5 * time.Second) continue } meshNodeID = utils.StripAll(pMesh[0]) //a.Logger.Debugln("Node id:", meshNodeID) if strings.Contains(strings.ToLower(meshNodeID), "not defined") { //a.Logger.Errorln(meshNodeID) time.Sleep(5 * time.Second) continue } meshSuccess = true } return meshNodeID, nil } func Start(_ service.Service) error { go rpc.RunRPC(NewAgentConfig()) return nil } func GetPython(force bool) { if trmm.FileExists(system.GetPythonBin()) && !force { return } var archZip string var folder string switch runtime.GOARCH { case "amd64": archZip = "py38-x64.zip" folder = "py38-x64" case "386": archZip = "py38-x32.zip" folder = "py38-x32" } pyFolder := filepath.Join(system.GetProgramDirectory(), folder) pyZip := filepath.Join(system.GetProgramDirectory(), archZip) //a.Logger.Debugln(pyZip) //a.Logger.Debugln(a.PyBin) defer os.Remove(pyZip) if force { os.RemoveAll(pyFolder) } config := NewAgentConfig() rClient := resty.New() rClient.SetTimeout(20 * time.Minute) rClient.SetRetryCount(10) rClient.SetRetryWaitTime(1 * time.Minute) rClient.SetRetryMaxWaitTime(15 * time.Minute) if len(config.Proxy) > 0 { rClient.SetProxy(config.Proxy) } url := fmt.Sprintf("https://github.com/amidaware/rmmagent/releases/download/v2.0.0/%s", archZip) //a.Logger.Debugln(url) r, err := rClient.R().SetOutput(pyZip).Get(url) if err != nil { //a.Logger.Errorln("Unable to download py3.zip from github.", err) return } if r.IsError() { //a.Logger.Errorln("Unable to download py3.zip from github. Status code", r.StatusCode()) return } err = utils.Unzip(pyZip, system.GetProgramDirectory()) if err != nil { //a.Logger.Errorln(err) } }