big refactor
This commit is contained in:
parent
6abf716844
commit
7fbb0fe7e1
25 changed files with 2761 additions and 822 deletions
103
agent/agent.go
103
agent/agent.go
|
|
@ -213,111 +213,8 @@ func (a *Agent) SyncMeshNodeID() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Agent) setupNatsOptions() []nats.Option {
|
|
||||||
opts := make([]nats.Option, 0)
|
|
||||||
opts = append(opts, nats.Name("TacticalRMM"))
|
|
||||||
opts = append(opts, nats.UserInfo(a.AgentID, a.Token))
|
|
||||||
opts = append(opts, nats.ReconnectWait(time.Second*5))
|
|
||||||
opts = append(opts, nats.RetryOnFailedConnect(true))
|
|
||||||
opts = append(opts, nats.MaxReconnects(-1))
|
|
||||||
opts = append(opts, nats.ReconnectBufSize(-1))
|
|
||||||
return opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Agent) GetUninstallExe() string {
|
|
||||||
cderr := os.Chdir(a.ProgramDir)
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Agent) CleanupAgentUpdates() {
|
|
||||||
cderr := os.Chdir(a.ProgramDir)
|
|
||||||
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 (a *Agent) RunPythonCode(code string, timeout int, args []string) (string, error) {
|
|
||||||
content := []byte(code)
|
|
||||||
dir, err := ioutil.TempDir("", "tacticalpy")
|
|
||||||
if err != nil {
|
|
||||||
a.Logger.Debugln(err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
tmpfn, _ := ioutil.TempFile(dir, "*.py")
|
|
||||||
if _, err := tmpfn.Write(content); err != nil {
|
|
||||||
a.Logger.Debugln(err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if err := tmpfn.Close(); err != nil {
|
|
||||||
a.Logger.Debugln(err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
var outb, errb bytes.Buffer
|
|
||||||
cmdArgs := []string{tmpfn.Name()}
|
|
||||||
if len(args) > 0 {
|
|
||||||
cmdArgs = append(cmdArgs, args...)
|
|
||||||
}
|
|
||||||
a.Logger.Debugln(cmdArgs)
|
|
||||||
cmd := exec.CommandContext(ctx, a.PyBin, cmdArgs...)
|
|
||||||
cmd.Stdout = &outb
|
|
||||||
cmd.Stderr = &errb
|
|
||||||
|
|
||||||
cmdErr := cmd.Run()
|
|
||||||
|
|
||||||
if ctx.Err() == context.DeadlineExceeded {
|
|
||||||
a.Logger.Debugln("RunPythonCode:", ctx.Err())
|
|
||||||
return "", ctx.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
if cmdErr != nil {
|
|
||||||
a.Logger.Debugln("RunPythonCode:", cmdErr)
|
|
||||||
return "", cmdErr
|
|
||||||
}
|
|
||||||
|
|
||||||
if errb.String() != "" {
|
|
||||||
a.Logger.Debugln(errb.String())
|
|
||||||
return errb.String(), errors.New("RunPythonCode stderr")
|
|
||||||
}
|
|
||||||
|
|
||||||
return outb.String(), nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Agent) CreateTRMMTempDir() {
|
func (a *Agent) CreateTRMMTempDir() {
|
||||||
// create the temp dir for running scripts
|
// create the temp dir for running scripts
|
||||||
|
|
|
||||||
|
|
@ -42,607 +42,15 @@ import (
|
||||||
"golang.org/x/sys/windows/registry"
|
"golang.org/x/sys/windows/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
getDriveType = windows.NewLazySystemDLL("kernel32.dll").NewProc("GetDriveTypeW")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func SetDetached() *syscall.SysProcAttr {
|
|
||||||
return &syscall.SysProcAttr{
|
|
||||||
CreationFlags: windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func CMD(exe string, args []string, timeout int, detached bool) (output [2]string, e error) {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
var outb, errb bytes.Buffer
|
|
||||||
cmd := exec.CommandContext(ctx, exe, args...)
|
|
||||||
if detached {
|
|
||||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
|
||||||
CreationFlags: windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmd.Stdout = &outb
|
|
||||||
cmd.Stderr = &errb
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
return [2]string{"", ""}, fmt.Errorf("%s: %s", err, CleanString(errb.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.Err() == context.DeadlineExceeded {
|
|
||||||
return [2]string{"", ""}, ctx.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
return [2]string{CleanString(outb.String()), CleanString(errb.String())}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func CMDShell(shell string, cmdArgs []string, command string, timeout int, detached bool) (output [2]string, e error) {
|
|
||||||
var (
|
|
||||||
outb bytes.Buffer
|
|
||||||
errb bytes.Buffer
|
|
||||||
cmd *exec.Cmd
|
|
||||||
timedOut = false
|
|
||||||
)
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if len(cmdArgs) > 0 && command == "" {
|
|
||||||
switch shell {
|
|
||||||
case "cmd":
|
|
||||||
cmdArgs = append([]string{"/C"}, cmdArgs...)
|
|
||||||
cmd = exec.Command("cmd.exe", cmdArgs...)
|
|
||||||
case "powershell":
|
|
||||||
cmdArgs = append([]string{"-NonInteractive", "-NoProfile"}, cmdArgs...)
|
|
||||||
cmd = exec.Command("powershell.exe", cmdArgs...)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch shell {
|
|
||||||
case "cmd":
|
|
||||||
cmd = exec.Command("cmd.exe")
|
|
||||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
|
||||||
CmdLine: fmt.Sprintf("cmd.exe /C %s", command),
|
|
||||||
}
|
|
||||||
case "powershell":
|
|
||||||
cmd = exec.Command("Powershell", "-NonInteractive", "-NoProfile", command)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
|
|
||||||
if detached {
|
|
||||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
|
||||||
CreationFlags: windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmd.Stdout = &outb
|
|
||||||
cmd.Stderr = &errb
|
|
||||||
cmd.Start()
|
|
||||||
|
|
||||||
pid := int32(cmd.Process.Pid)
|
|
||||||
|
|
||||||
go func(p int32) {
|
|
||||||
|
|
||||||
<-ctx.Done()
|
|
||||||
|
|
||||||
_ = KillProc(p)
|
|
||||||
timedOut = true
|
|
||||||
}(pid)
|
|
||||||
|
|
||||||
err := cmd.Wait()
|
|
||||||
|
|
||||||
if timedOut {
|
|
||||||
return [2]string{CleanString(outb.String()), CleanString(errb.String())}, ctx.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return [2]string{CleanString(outb.String()), CleanString(errb.String())}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return [2]string{CleanString(outb.String()), CleanString(errb.String())}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDisks returns a list of fixed disks
|
|
||||||
func (a *Agent) GetDisks() []trmm.Disk {
|
|
||||||
ret := make([]trmm.Disk, 0)
|
|
||||||
partitions, err := disk.Partitions(false)
|
|
||||||
if err != nil {
|
|
||||||
a.Logger.Debugln(err)
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range partitions {
|
|
||||||
typepath, _ := windows.UTF16PtrFromString(p.Device)
|
|
||||||
typeval, _, _ := getDriveType.Call(uintptr(unsafe.Pointer(typepath)))
|
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getdrivetypea
|
|
||||||
if typeval != 3 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
usage, err := disk.Usage(p.Mountpoint)
|
|
||||||
if err != nil {
|
|
||||||
a.Logger.Debugln(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
d := trmm.Disk{
|
|
||||||
Device: p.Device,
|
|
||||||
Fstype: p.Fstype,
|
|
||||||
Total: ByteCountSI(usage.Total),
|
|
||||||
Used: ByteCountSI(usage.Used),
|
|
||||||
Free: ByteCountSI(usage.Free),
|
|
||||||
Percent: int(usage.UsedPercent),
|
|
||||||
}
|
|
||||||
ret = append(ret, d)
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoggedOnUser returns the first logged on user it finds
|
|
||||||
func (a *Agent) LoggedOnUser() string {
|
|
||||||
pyCode := `
|
|
||||||
import psutil
|
|
||||||
|
|
||||||
try:
|
|
||||||
u = psutil.users()[0].name
|
|
||||||
if u.isascii():
|
|
||||||
print(u, end='')
|
|
||||||
else:
|
|
||||||
print('notascii', end='')
|
|
||||||
except Exception as e:
|
|
||||||
print("None", end='')
|
|
||||||
|
|
||||||
`
|
|
||||||
// try with psutil first, if fails, fallback to golang
|
|
||||||
user, err := a.RunPythonCode(pyCode, 5, []string{})
|
|
||||||
if err == nil && user != "notascii" {
|
|
||||||
return user
|
|
||||||
}
|
|
||||||
|
|
||||||
users, err := wapf.ListLoggedInUsers()
|
|
||||||
if err != nil {
|
|
||||||
a.Logger.Debugln("LoggedOnUser error", err)
|
|
||||||
return "None"
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(users) == 0 {
|
|
||||||
return "None"
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, u := range users {
|
|
||||||
// remove the computername or domain
|
|
||||||
return strings.Split(u.FullUser(), `\`)[1]
|
|
||||||
}
|
|
||||||
return "None"
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShowStatus prints windows service status
|
|
||||||
// If called from an interactive desktop, pops up a message box
|
|
||||||
// Otherwise prints to the console
|
|
||||||
func ShowStatus(version string) {
|
|
||||||
statusMap := make(map[string]string)
|
|
||||||
svcs := []string{winSvcName, meshSvcName}
|
|
||||||
|
|
||||||
for _, service := range svcs {
|
|
||||||
status, err := GetServiceStatus(service)
|
|
||||||
if err != nil {
|
|
||||||
statusMap[service] = "Not Installed"
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
statusMap[service] = status
|
|
||||||
}
|
|
||||||
|
|
||||||
window := w32.GetForegroundWindow()
|
|
||||||
if window != 0 {
|
|
||||||
_, consoleProcID := w32.GetWindowThreadProcessId(window)
|
|
||||||
if w32.GetCurrentProcessId() == consoleProcID {
|
|
||||||
w32.ShowWindow(window, w32.SW_HIDE)
|
|
||||||
}
|
|
||||||
var handle w32.HWND
|
|
||||||
msg := fmt.Sprintf("Agent: %s\n\nMesh Agent: %s", statusMap[winSvcName], statusMap[meshSvcName])
|
|
||||||
w32.MessageBox(handle, msg, fmt.Sprintf("Tactical RMM v%s", version), w32.MB_OK|w32.MB_ICONINFORMATION)
|
|
||||||
} else {
|
|
||||||
fmt.Println("Tactical RMM Version", version)
|
|
||||||
fmt.Println("Tactical Agent:", statusMap[winSvcName])
|
|
||||||
fmt.Println("Mesh Agent:", statusMap[meshSvcName])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PatchMgmnt enables/disables automatic update
|
|
||||||
// 0 - Enable Automatic Updates (Default)
|
|
||||||
// 1 - Disable Automatic Updates
|
|
||||||
// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd939844(v=ws.10)?redirectedfrom=MSDN
|
|
||||||
func (a *Agent) PatchMgmnt(enable bool) error {
|
|
||||||
var val uint32
|
|
||||||
k, _, err := registry.CreateKey(registry.LOCAL_MACHINE, `SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU`, registry.ALL_ACCESS)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if enable {
|
|
||||||
val = 1
|
|
||||||
} else {
|
|
||||||
val = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
err = k.SetDWordValue("AUOptions", val)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Agent) PlatVer() (string, error) {
|
|
||||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.ALL_ACCESS)
|
|
||||||
if err != nil {
|
|
||||||
return "n/a", err
|
|
||||||
}
|
|
||||||
defer k.Close()
|
|
||||||
|
|
||||||
dv, _, err := k.GetStringValue("DisplayVersion")
|
|
||||||
if err == nil {
|
|
||||||
return dv, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
relid, _, err := k.GetStringValue("ReleaseId")
|
|
||||||
if err != nil {
|
|
||||||
return "n/a", err
|
|
||||||
}
|
|
||||||
return relid, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnablePing enables ping
|
|
||||||
func EnablePing() {
|
|
||||||
args := make([]string, 0)
|
|
||||||
cmd := `netsh advfirewall firewall add rule name="ICMP Allow incoming V4 echo request" protocol=icmpv4:8,any dir=in action=allow`
|
|
||||||
_, err := CMDShell("cmd", args, cmd, 10, false)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableRDP enables Remote Desktop
|
|
||||||
func EnableRDP() {
|
|
||||||
k, _, err := registry.CreateKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Control\Terminal Server`, registry.ALL_ACCESS)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
defer k.Close()
|
|
||||||
|
|
||||||
err = k.SetDWordValue("fDenyTSConnections", 0)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := make([]string, 0)
|
|
||||||
cmd := `netsh advfirewall firewall set rule group="remote desktop" new enable=Yes`
|
|
||||||
_, cerr := CMDShell("cmd", args, cmd, 10, false)
|
|
||||||
if cerr != nil {
|
|
||||||
fmt.Println(cerr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisableSleepHibernate disables sleep and hibernate
|
|
||||||
func DisableSleepHibernate() {
|
|
||||||
k, _, err := registry.CreateKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Control\Session Manager\Power`, registry.ALL_ACCESS)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
defer k.Close()
|
|
||||||
|
|
||||||
err = k.SetDWordValue("HiberbootEnabled", 0)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := make([]string, 0)
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
currents := []string{"ac", "dc"}
|
|
||||||
for _, i := range currents {
|
|
||||||
wg.Add(1)
|
|
||||||
go func(c string) {
|
|
||||||
defer wg.Done()
|
|
||||||
_, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /set%svalueindex scheme_current sub_buttons lidaction 0", c), 5, false)
|
|
||||||
_, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -standby-timeout-%s 0", c), 5, false)
|
|
||||||
_, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -hibernate-timeout-%s 0", c), 5, false)
|
|
||||||
_, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -disk-timeout-%s 0", c), 5, false)
|
|
||||||
_, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -monitor-timeout-%s 0", c), 5, false)
|
|
||||||
}(i)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
_, _ = CMDShell("cmd", args, "powercfg -S SCHEME_CURRENT", 5, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCOMObject creates a new COM object for the specifed ProgramID.
|
|
||||||
func NewCOMObject(id string) (*ole.IDispatch, error) {
|
|
||||||
unknown, err := oleutil.CreateObject(id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to create initial unknown object: %v", err)
|
|
||||||
}
|
|
||||||
defer unknown.Release()
|
|
||||||
|
|
||||||
obj, err := unknown.QueryInterface(ole.IID_IDispatch)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to create query interface: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SystemRebootRequired checks whether a system reboot is required.
|
|
||||||
func (a *Agent) SystemRebootRequired() (bool, error) {
|
|
||||||
regKeys := []string{
|
|
||||||
`SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired`,
|
|
||||||
}
|
|
||||||
for _, key := range regKeys {
|
|
||||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, key, registry.QUERY_VALUE)
|
|
||||||
if err == nil {
|
|
||||||
k.Close()
|
|
||||||
return true, nil
|
|
||||||
} else if err != registry.ErrNotExist {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Agent) SendSoftware() {
|
|
||||||
sw := a.GetInstalledSoftware()
|
|
||||||
a.Logger.Debugln(sw)
|
|
||||||
|
|
||||||
payload := map[string]interface{}{"agent_id": a.AgentID, "software": sw}
|
|
||||||
_, err := a.rClient.R().SetBody(payload).Post("/api/v3/software/")
|
|
||||||
if err != nil {
|
|
||||||
a.Logger.Debugln(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Agent) UninstallCleanup() {
|
|
||||||
registry.DeleteKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`)
|
|
||||||
a.PatchMgmnt(false)
|
|
||||||
a.CleanupAgentUpdates()
|
|
||||||
CleanupSchedTasks()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Agent) AgentUpdate(url, inno, version string) {
|
|
||||||
time.Sleep(time.Duration(randRange(1, 15)) * time.Second)
|
|
||||||
a.KillHungUpdates()
|
|
||||||
a.CleanupAgentUpdates()
|
|
||||||
updater := filepath.Join(a.ProgramDir, inno)
|
|
||||||
a.Logger.Infof("Agent updating from %s to %s", a.Version, version)
|
|
||||||
a.Logger.Infoln("Downloading agent update from", url)
|
|
||||||
|
|
||||||
rClient := resty.New()
|
|
||||||
rClient.SetCloseConnection(true)
|
|
||||||
rClient.SetTimeout(15 * time.Minute)
|
|
||||||
rClient.SetDebug(a.Debug)
|
|
||||||
if len(a.Proxy) > 0 {
|
|
||||||
rClient.SetProxy(a.Proxy)
|
|
||||||
}
|
|
||||||
r, err := rClient.R().SetOutput(updater).Get(url)
|
|
||||||
if err != nil {
|
|
||||||
a.Logger.Errorln(err)
|
|
||||||
CMD("net", []string{"start", winSvcName}, 10, false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if r.IsError() {
|
|
||||||
a.Logger.Errorln("Download failed with status code", r.StatusCode())
|
|
||||||
CMD("net", []string{"start", winSvcName}, 10, false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
dir, err := ioutil.TempDir("", "tacticalrmm")
|
|
||||||
if err != nil {
|
|
||||||
a.Logger.Errorln("Agentupdate create tempdir:", err)
|
|
||||||
CMD("net", []string{"start", 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 (a *Agent) osString() string {
|
|
||||||
host, _ := ps.Host()
|
|
||||||
info := host.Info()
|
|
||||||
osInf := info.OS
|
|
||||||
|
|
||||||
var arch string
|
|
||||||
switch info.Architecture {
|
|
||||||
case "x86_64":
|
|
||||||
arch = "64 bit"
|
|
||||||
case "x86":
|
|
||||||
arch = "32 bit"
|
|
||||||
}
|
|
||||||
|
|
||||||
var osFullName string
|
|
||||||
platver, err := a.PlatVer()
|
|
||||||
if err != nil {
|
|
||||||
osFullName = fmt.Sprintf("%s, %s (build %s)", osInf.Name, arch, osInf.Build)
|
|
||||||
} else {
|
|
||||||
osFullName = fmt.Sprintf("%s, %s v%s (build %s)", osInf.Name, arch, platver, osInf.Build)
|
|
||||||
}
|
|
||||||
return osFullName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Agent) AgentUninstall(code string) {
|
|
||||||
a.KillHungUpdates()
|
|
||||||
tacUninst := filepath.Join(a.ProgramDir, a.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 (a *Agent) addDefenderExlusions() {
|
|
||||||
code := `
|
|
||||||
Add-MpPreference -ExclusionPath 'C:\Program Files\TacticalAgent\*'
|
|
||||||
Add-MpPreference -ExclusionPath 'C:\Windows\Temp\winagent-v*.exe'
|
|
||||||
Add-MpPreference -ExclusionPath 'C:\Windows\Temp\trmm\*'
|
|
||||||
Add-MpPreference -ExclusionPath 'C:\Program Files\Mesh Agent\*'
|
|
||||||
`
|
|
||||||
_, _, _, err := a.RunScript(code, "powershell", []string{}, 20)
|
|
||||||
if err != nil {
|
|
||||||
a.Logger.Debugln(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunMigrations cleans up unused stuff from older agents
|
|
||||||
func (a *Agent) RunMigrations() {
|
|
||||||
for _, i := range []string{"nssm.exe", "nssm-x86.exe"} {
|
|
||||||
nssm := filepath.Join(a.ProgramDir, i)
|
|
||||||
if trmm.FileExists(nssm) {
|
|
||||||
os.Remove(nssm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Agent) 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 := 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 := 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 = 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChecksRunning prevents duplicate checks from running
|
|
||||||
// Have to do it this way, can't use atomic because they can run from both rpc and tacticalagent services
|
|
||||||
func (a *Agent) ChecksRunning() bool {
|
|
||||||
running := false
|
|
||||||
procs, err := ps.Processes()
|
|
||||||
if err != nil {
|
|
||||||
return running
|
|
||||||
}
|
|
||||||
|
|
||||||
Out:
|
|
||||||
for _, process := range procs {
|
|
||||||
p, err := process.Info()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if p.PID == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if p.Exe != a.EXE {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, arg := range p.Args {
|
|
||||||
if arg == "runchecks" || arg == "checkrunner" {
|
|
||||||
running = true
|
|
||||||
break Out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return running
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Agent) GetPython(force bool) {
|
|
||||||
if trmm.FileExists(a.PyBin) && !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(a.ProgramDir, folder)
|
|
||||||
pyZip := filepath.Join(a.ProgramDir, archZip)
|
|
||||||
a.Logger.Debugln(pyZip)
|
|
||||||
a.Logger.Debugln(a.PyBin)
|
|
||||||
defer os.Remove(pyZip)
|
|
||||||
|
|
||||||
if force {
|
|
||||||
os.RemoveAll(pyFolder)
|
|
||||||
}
|
|
||||||
|
|
||||||
rClient := resty.New()
|
|
||||||
rClient.SetTimeout(20 * time.Minute)
|
|
||||||
rClient.SetRetryCount(10)
|
|
||||||
rClient.SetRetryWaitTime(1 * time.Minute)
|
|
||||||
rClient.SetRetryMaxWaitTime(15 * time.Minute)
|
|
||||||
if len(a.Proxy) > 0 {
|
|
||||||
rClient.SetProxy(a.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 = Unzip(pyZip, a.ProgramDir)
|
|
||||||
if err != nil {
|
|
||||||
a.Logger.Errorln(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Agent) RecoverMesh() {
|
func (a *Agent) RecoverMesh() {
|
||||||
a.Logger.Infoln("Attempting mesh recovery")
|
a.Logger.Infoln("Attempting mesh recovery")
|
||||||
|
|
@ -653,33 +61,9 @@ func (a *Agent) RecoverMesh() {
|
||||||
a.SyncMeshNodeID()
|
a.SyncMeshNodeID()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Agent) getMeshNodeID() (string, error) {
|
|
||||||
out, err := CMD(a.MeshSystemBin, []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(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 (a *Agent) Start(_ service.Service) error {
|
|
||||||
go a.RunRPC()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Agent) Stop(_ service.Service) error {
|
func (a *Agent) Stop(_ service.Service) error {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
50
agent/disk/disk_windows.go
Normal file
50
agent/disk/disk_windows.go
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
package disk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
|
"github.com/shirou/gopsutil/disk"
|
||||||
|
trmm "github.com/wh1te909/trmm-shared"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
getDriveType = windows.NewLazySystemDLL("kernel32.dll").NewProc("GetDriveTypeW")
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetDisks returns a list of fixed disks
|
||||||
|
func GetDisks() []trmm.Disk {
|
||||||
|
ret := make([]trmm.Disk, 0)
|
||||||
|
partitions, err := disk.Partitions(false)
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range partitions {
|
||||||
|
typepath, _ := windows.UTF16PtrFromString(p.Device)
|
||||||
|
typeval, _, _ := getDriveType.Call(uintptr(unsafe.Pointer(typepath)))
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getdrivetypea
|
||||||
|
if typeval != 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
usage, err := disk.Usage(p.Mountpoint)
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
d := trmm.Disk{
|
||||||
|
Device: p.Device,
|
||||||
|
Fstype: p.Fstype,
|
||||||
|
Total: utils.ByteCountSI(usage.Total),
|
||||||
|
Used: utils.ByteCountSI(usage.Used),
|
||||||
|
Free: utils.ByteCountSI(usage.Free),
|
||||||
|
Percent: int(usage.UsedPercent),
|
||||||
|
}
|
||||||
|
ret = append(ret, d)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
28
agent/patching/patching_windows.go
Normal file
28
agent/patching/patching_windows.go
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
package patching
|
||||||
|
|
||||||
|
import "golang.org/x/sys/windows/registry"
|
||||||
|
|
||||||
|
// PatchMgmnt enables/disables automatic update
|
||||||
|
// 0 - Enable Automatic Updates (Default)
|
||||||
|
// 1 - Disable Automatic Updates
|
||||||
|
// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd939844(v=ws.10)?redirectedfrom=MSDN
|
||||||
|
func PatchMgmnt(enable bool) error {
|
||||||
|
var val uint32
|
||||||
|
k, _, err := registry.CreateKey(registry.LOCAL_MACHINE, `SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU`, registry.ALL_ACCESS)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if enable {
|
||||||
|
val = 1
|
||||||
|
} else {
|
||||||
|
val = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
err = k.SetDWordValue("AUOptions", val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
473
agent/rpc/rpc_windows.go
Normal file
473
agent/rpc/rpc_windows.go
Normal file
|
|
@ -0,0 +1,473 @@
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
rmm "github.com/amidaware/rmmagent/shared"
|
||||||
|
nats "github.com/nats-io/nats.go"
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RunRPC(version string) {
|
||||||
|
//a.Logger.Infoln("Agent service started")
|
||||||
|
go service.RunAsService()
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
opts := a.setupNatsOptions()
|
||||||
|
server := fmt.Sprintf("tls://%s:4222", a.ApiURL)
|
||||||
|
nc, err := nats.Connect(server, opts...)
|
||||||
|
if err != nil {
|
||||||
|
a.Logger.Fatalln("RunRPC() nats.Connect()", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nc.Subscribe(a.AgentID, func(msg *nats.Msg) {
|
||||||
|
var payload *NatsMsg
|
||||||
|
var mh codec.MsgpackHandle
|
||||||
|
mh.RawToString = true
|
||||||
|
|
||||||
|
dec := codec.NewDecoderBytes(msg.Data, &mh)
|
||||||
|
if err := dec.Decode(&payload); err != nil {
|
||||||
|
a.Logger.Errorln(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch payload.Func {
|
||||||
|
case "ping":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
a.Logger.Debugln("pong")
|
||||||
|
ret.Encode("pong")
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
|
||||||
|
case "patchmgmt":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
err := a.PatchMgmnt(p.PatchMgmt)
|
||||||
|
if err != nil {
|
||||||
|
a.Logger.Errorln("PatchMgmnt:", err.Error())
|
||||||
|
ret.Encode(err.Error())
|
||||||
|
} else {
|
||||||
|
ret.Encode("ok")
|
||||||
|
}
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "schedtask":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
success, err := a.CreateSchedTask(p.ScheduledTask)
|
||||||
|
if err != nil {
|
||||||
|
a.Logger.Errorln(err.Error())
|
||||||
|
ret.Encode(err.Error())
|
||||||
|
} else if !success {
|
||||||
|
ret.Encode("Something went wrong")
|
||||||
|
} else {
|
||||||
|
ret.Encode("ok")
|
||||||
|
}
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "delschedtask":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
err := DeleteSchedTask(p.ScheduledTask.Name)
|
||||||
|
if err != nil {
|
||||||
|
a.Logger.Errorln(err.Error())
|
||||||
|
ret.Encode(err.Error())
|
||||||
|
} else {
|
||||||
|
ret.Encode("ok")
|
||||||
|
}
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "listschedtasks":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
tasks := ListSchedTasks()
|
||||||
|
a.Logger.Debugln(tasks)
|
||||||
|
ret.Encode(tasks)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
|
||||||
|
case "eventlog":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
days, _ := strconv.Atoi(p.Data["days"])
|
||||||
|
evtLog := a.GetEventLog(p.Data["logname"], days)
|
||||||
|
a.Logger.Debugln(evtLog)
|
||||||
|
ret.Encode(evtLog)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "procs":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
procs := a.GetProcsRPC()
|
||||||
|
a.Logger.Debugln(procs)
|
||||||
|
ret.Encode(procs)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
|
||||||
|
case "killproc":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
err := KillProc(p.ProcPID)
|
||||||
|
if err != nil {
|
||||||
|
ret.Encode(err.Error())
|
||||||
|
a.Logger.Debugln(err.Error())
|
||||||
|
} else {
|
||||||
|
ret.Encode("ok")
|
||||||
|
}
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "rawcmd":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
var resultData rmm.RawCMDResp
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
out, _ := CMDShell(p.Data["shell"], []string{}, p.Data["command"], p.Timeout, false)
|
||||||
|
a.Logger.Debugln(out)
|
||||||
|
if out[1] != "" {
|
||||||
|
ret.Encode(out[1])
|
||||||
|
resultData.Results = out[1]
|
||||||
|
} else {
|
||||||
|
ret.Encode(out[0])
|
||||||
|
resultData.Results = out[0]
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
opts := a.NewCMDOpts()
|
||||||
|
opts.Shell = p.Data["shell"]
|
||||||
|
opts.Command = p.Data["command"]
|
||||||
|
opts.Timeout = time.Duration(p.Timeout)
|
||||||
|
out := a.CmdV2(opts)
|
||||||
|
tmp := ""
|
||||||
|
if len(out.Stdout) > 0 {
|
||||||
|
tmp += out.Stdout
|
||||||
|
}
|
||||||
|
if len(out.Stderr) > 0 {
|
||||||
|
tmp += "\n"
|
||||||
|
tmp += out.Stderr
|
||||||
|
}
|
||||||
|
ret.Encode(tmp)
|
||||||
|
resultData.Results = tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Respond(resp)
|
||||||
|
if p.ID != 0 {
|
||||||
|
a.rClient.R().SetBody(resultData).Patch(fmt.Sprintf("/api/v3/%d/%s/histresult/", p.ID, a.AgentID))
|
||||||
|
}
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "winservices":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
svcs := a.GetServices()
|
||||||
|
a.Logger.Debugln(svcs)
|
||||||
|
ret.Encode(svcs)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
|
||||||
|
case "winsvcdetail":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
svc := a.GetServiceDetail(p.Data["name"])
|
||||||
|
a.Logger.Debugln(svc)
|
||||||
|
ret.Encode(svc)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "winsvcaction":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
retData := a.ControlService(p.Data["name"], p.Data["action"])
|
||||||
|
a.Logger.Debugln(retData)
|
||||||
|
ret.Encode(retData)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "editwinsvc":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
retData := a.EditService(p.Data["name"], p.Data["startType"])
|
||||||
|
a.Logger.Debugln(retData)
|
||||||
|
ret.Encode(retData)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "runscript":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
var retData string
|
||||||
|
var resultData rmm.RunScriptResp
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
start := time.Now()
|
||||||
|
stdout, stderr, retcode, err := a.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout)
|
||||||
|
resultData.ExecTime = time.Since(start).Seconds()
|
||||||
|
resultData.ID = p.ID
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
a.Logger.Debugln(err)
|
||||||
|
retData = err.Error()
|
||||||
|
resultData.Retcode = 1
|
||||||
|
resultData.Stderr = err.Error()
|
||||||
|
} else {
|
||||||
|
retData = stdout + stderr // to keep backwards compat
|
||||||
|
resultData.Retcode = retcode
|
||||||
|
resultData.Stdout = stdout
|
||||||
|
resultData.Stderr = stderr
|
||||||
|
}
|
||||||
|
a.Logger.Debugln(retData)
|
||||||
|
ret.Encode(retData)
|
||||||
|
msg.Respond(resp)
|
||||||
|
if p.ID != 0 {
|
||||||
|
results := map[string]interface{}{"script_results": resultData}
|
||||||
|
a.rClient.R().SetBody(results).Patch(fmt.Sprintf("/api/v3/%d/%s/histresult/", p.ID, a.AgentID))
|
||||||
|
}
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "runscriptfull":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
var retData rmm.RunScriptResp
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
start := time.Now()
|
||||||
|
stdout, stderr, retcode, err := a.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout)
|
||||||
|
|
||||||
|
retData.ExecTime = time.Since(start).Seconds()
|
||||||
|
if err != nil {
|
||||||
|
retData.Stderr = err.Error()
|
||||||
|
retData.Retcode = 1
|
||||||
|
} else {
|
||||||
|
retData.Stdout = stdout
|
||||||
|
retData.Stderr = stderr
|
||||||
|
retData.Retcode = retcode
|
||||||
|
}
|
||||||
|
retData.ID = p.ID
|
||||||
|
a.Logger.Debugln(retData)
|
||||||
|
ret.Encode(retData)
|
||||||
|
msg.Respond(resp)
|
||||||
|
if p.ID != 0 {
|
||||||
|
results := map[string]interface{}{"script_results": retData}
|
||||||
|
a.rClient.R().SetBody(results).Patch(fmt.Sprintf("/api/v3/%d/%s/histresult/", p.ID, a.AgentID))
|
||||||
|
}
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "recover":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
|
||||||
|
switch p.Data["mode"] {
|
||||||
|
case "mesh":
|
||||||
|
a.Logger.Debugln("Recovering mesh")
|
||||||
|
a.RecoverMesh()
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
case "softwarelist":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
sw := a.GetInstalledSoftware()
|
||||||
|
a.Logger.Debugln(sw)
|
||||||
|
ret.Encode(sw)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
|
||||||
|
case "rebootnow":
|
||||||
|
go func() {
|
||||||
|
a.Logger.Debugln("Scheduling immediate reboot")
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
CMD("shutdown.exe", []string{"/r", "/t", "5", "/f"}, 15, false)
|
||||||
|
} else {
|
||||||
|
opts := a.NewCMDOpts()
|
||||||
|
opts.Command = "reboot"
|
||||||
|
a.CmdV2(opts)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
case "needsreboot":
|
||||||
|
go func() {
|
||||||
|
a.Logger.Debugln("Checking if reboot needed")
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
out, err := a.SystemRebootRequired()
|
||||||
|
if err == nil {
|
||||||
|
a.Logger.Debugln("Reboot needed:", out)
|
||||||
|
ret.Encode(out)
|
||||||
|
} else {
|
||||||
|
a.Logger.Debugln("Error checking if reboot needed:", err)
|
||||||
|
ret.Encode(false)
|
||||||
|
}
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
case "sysinfo":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
a.Logger.Debugln("Getting sysinfo with WMI")
|
||||||
|
modes := []string{"agent-agentinfo", "agent-disks", "agent-wmi", "agent-publicip"}
|
||||||
|
for _, m := range modes {
|
||||||
|
a.NatsMessage(nc, m)
|
||||||
|
}
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
case "wmi":
|
||||||
|
go func() {
|
||||||
|
a.Logger.Debugln("Sending WMI")
|
||||||
|
a.NatsMessage(nc, "agent-wmi")
|
||||||
|
}()
|
||||||
|
case "cpuloadavg":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
a.Logger.Debugln("Getting CPU Load Avg")
|
||||||
|
loadAvg := a.GetCPULoadAvg()
|
||||||
|
a.Logger.Debugln("CPU Load Avg:", loadAvg)
|
||||||
|
ret.Encode(loadAvg)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
case "runchecks":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
if a.ChecksRunning() {
|
||||||
|
ret.Encode("busy")
|
||||||
|
msg.Respond(resp)
|
||||||
|
a.Logger.Debugln("Checks are already running, please wait")
|
||||||
|
} else {
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
a.Logger.Debugln("Running checks")
|
||||||
|
_, checkerr := CMD(a.EXE, []string{"-m", "runchecks"}, 600, false)
|
||||||
|
if checkerr != nil {
|
||||||
|
a.Logger.Errorln("RPC RunChecks", checkerr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
a.Logger.Debugln("Running checks")
|
||||||
|
a.RunChecks(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
case "runtask":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
a.Logger.Debugln("Running task")
|
||||||
|
a.RunTask(p.TaskPK)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "publicip":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
ret.Encode(a.PublicIP())
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
case "installpython":
|
||||||
|
go a.GetPython(true)
|
||||||
|
case "installchoco":
|
||||||
|
go a.InstallChoco()
|
||||||
|
case "installwithchoco":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
out, _ := a.InstallWithChoco(p.ChocoProgName)
|
||||||
|
results := map[string]string{"results": out}
|
||||||
|
url := fmt.Sprintf("/api/v3/%d/chocoresult/", p.PendingActionPK)
|
||||||
|
a.rClient.R().SetBody(results).Patch(url)
|
||||||
|
}(payload)
|
||||||
|
case "getwinupdates":
|
||||||
|
go func() {
|
||||||
|
if !atomic.CompareAndSwapUint32(&getWinUpdateLocker, 0, 1) {
|
||||||
|
a.Logger.Debugln("Already checking for windows updates")
|
||||||
|
} else {
|
||||||
|
a.Logger.Debugln("Checking for windows updates")
|
||||||
|
defer atomic.StoreUint32(&getWinUpdateLocker, 0)
|
||||||
|
a.GetWinUpdates()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
case "installwinupdates":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
if !atomic.CompareAndSwapUint32(&installWinUpdateLocker, 0, 1) {
|
||||||
|
a.Logger.Debugln("Already installing windows updates")
|
||||||
|
} else {
|
||||||
|
a.Logger.Debugln("Installing windows updates", p.UpdateGUIDs)
|
||||||
|
defer atomic.StoreUint32(&installWinUpdateLocker, 0)
|
||||||
|
a.InstallUpdates(p.UpdateGUIDs)
|
||||||
|
}
|
||||||
|
}(payload)
|
||||||
|
case "agentupdate":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
if !atomic.CompareAndSwapUint32(&agentUpdateLocker, 0, 1) {
|
||||||
|
a.Logger.Debugln("Agent update already running")
|
||||||
|
ret.Encode("updaterunning")
|
||||||
|
msg.Respond(resp)
|
||||||
|
} else {
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
a.AgentUpdate(p.Data["url"], p.Data["inno"], p.Data["version"])
|
||||||
|
atomic.StoreUint32(&agentUpdateLocker, 0)
|
||||||
|
nc.Flush()
|
||||||
|
nc.Close()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "uninstall":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
a.AgentUninstall(p.Code)
|
||||||
|
nc.Flush()
|
||||||
|
nc.Close()
|
||||||
|
os.Exit(0)
|
||||||
|
}(payload)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
nc.Flush()
|
||||||
|
|
||||||
|
if err := nc.LastError(); err != nil {
|
||||||
|
a.Logger.Errorln(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
20
agent/rpc/structs.go
Normal file
20
agent/rpc/structs.go
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import "github.com/amidaware/rmmagent/agent/tasks"
|
||||||
|
|
||||||
|
type NatsMsg struct {
|
||||||
|
Func string `json:"func"`
|
||||||
|
Timeout int `json:"timeout"`
|
||||||
|
Data map[string]string `json:"payload"`
|
||||||
|
ScriptArgs []string `json:"script_args"`
|
||||||
|
ProcPID int32 `json:"procpid"`
|
||||||
|
TaskPK int `json:"taskpk"`
|
||||||
|
ScheduledTask tasks.SchedTask `json:"schedtaskpayload"`
|
||||||
|
RecoveryCommand string `json:"recoverycommand"`
|
||||||
|
UpdateGUIDs []string `json:"guids"`
|
||||||
|
ChocoProgName string `json:"choco_prog_name"`
|
||||||
|
PendingActionPK int `json:"pending_action_pk"`
|
||||||
|
PatchMgmt bool `json:"patch_mgmt"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
164
agent/services/services_windows.go
Normal file
164
agent/services/services_windows.go
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
|
"github.com/gonutz/w32/v2"
|
||||||
|
trmm "github.com/wh1te909/trmm-shared"
|
||||||
|
"golang.org/x/sys/windows/svc/mgr"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
WinSvcName = "tacticalrmm"
|
||||||
|
meshSvcName = "mesh agent"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ShowStatus prints windows service status
|
||||||
|
// If called from an interactive desktop, pops up a message box
|
||||||
|
// Otherwise prints to the console
|
||||||
|
func ShowStatus(version string) {
|
||||||
|
statusMap := make(map[string]string)
|
||||||
|
svcs := []string{WinSvcName, meshSvcName}
|
||||||
|
|
||||||
|
for _, service := range svcs {
|
||||||
|
status, err := GetServiceStatus(service)
|
||||||
|
if err != nil {
|
||||||
|
statusMap[service] = "Not Installed"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
statusMap[service] = status
|
||||||
|
}
|
||||||
|
|
||||||
|
window := w32.GetForegroundWindow()
|
||||||
|
if window != 0 {
|
||||||
|
_, consoleProcID := w32.GetWindowThreadProcessId(window)
|
||||||
|
if w32.GetCurrentProcessId() == consoleProcID {
|
||||||
|
w32.ShowWindow(window, w32.SW_HIDE)
|
||||||
|
}
|
||||||
|
var handle w32.HWND
|
||||||
|
msg := fmt.Sprintf("Agent: %s\n\nMesh Agent: %s", statusMap[WinSvcName], statusMap[meshSvcName])
|
||||||
|
w32.MessageBox(handle, msg, fmt.Sprintf("Tactical RMM v%s", version), w32.MB_OK|w32.MB_ICONINFORMATION)
|
||||||
|
} else {
|
||||||
|
fmt.Println("Tactical RMM Version", version)
|
||||||
|
fmt.Println("Tactical Agent:", statusMap[WinSvcName])
|
||||||
|
fmt.Println("Mesh Agent:", statusMap[meshSvcName])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetServiceStatus(name string) (string, error) {
|
||||||
|
conn, err := mgr.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return "n/a", err
|
||||||
|
}
|
||||||
|
defer conn.Disconnect()
|
||||||
|
|
||||||
|
srv, err := conn.OpenService(name)
|
||||||
|
if err != nil {
|
||||||
|
return "n/a", err
|
||||||
|
}
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
q, err := srv.Query()
|
||||||
|
if err != nil {
|
||||||
|
return "n/a", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return serviceStatusText(uint32(q.State)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicecontrollerstatus?view=dotnet-plat-ext-3.1
|
||||||
|
func serviceStatusText(num uint32) string {
|
||||||
|
switch num {
|
||||||
|
case 1:
|
||||||
|
return "stopped"
|
||||||
|
case 2:
|
||||||
|
return "start_pending"
|
||||||
|
case 3:
|
||||||
|
return "stop_pending"
|
||||||
|
case 4:
|
||||||
|
return "running"
|
||||||
|
case 5:
|
||||||
|
return "continue_pending"
|
||||||
|
case 6:
|
||||||
|
return "pause_pending"
|
||||||
|
case 7:
|
||||||
|
return "paused"
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetServices returns a list of windows services
|
||||||
|
func GetServices() []trmm.WindowsService {
|
||||||
|
ret := make([]trmm.WindowsService, 0)
|
||||||
|
|
||||||
|
conn, err := mgr.Connect()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
defer conn.Disconnect()
|
||||||
|
|
||||||
|
svcs, err := conn.ListServices()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range svcs {
|
||||||
|
srv, err := conn.OpenService(s)
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() != "Access is denied." {
|
||||||
|
//a.Logger.Debugln("Open Service", s, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
defer srv.Close()
|
||||||
|
q, err := srv.Query()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
conf, err := srv.Config()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(ret, trmm.WindowsService{
|
||||||
|
Name: s,
|
||||||
|
Status: serviceStatusText(uint32(q.State)),
|
||||||
|
DisplayName: utils.CleanString(conf.DisplayName),
|
||||||
|
BinPath: utils.CleanString(conf.BinaryPathName),
|
||||||
|
Description: utils.CleanString(conf.Description),
|
||||||
|
Username: utils.CleanString(conf.ServiceStartName),
|
||||||
|
PID: q.ProcessId,
|
||||||
|
StartType: serviceStartType(uint32(conf.StartType)),
|
||||||
|
DelayedAutoStart: conf.DelayedAutoStart,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicestartmode?view=dotnet-plat-ext-3.1
|
||||||
|
func serviceStartType(num uint32) string {
|
||||||
|
switch num {
|
||||||
|
case 0:
|
||||||
|
return "Boot"
|
||||||
|
case 1:
|
||||||
|
return "System"
|
||||||
|
case 2:
|
||||||
|
return "Automatic"
|
||||||
|
case 3:
|
||||||
|
return "Manual"
|
||||||
|
case 4:
|
||||||
|
return "Disabled"
|
||||||
|
default:
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,26 +20,7 @@ import (
|
||||||
"golang.org/x/sys/windows/svc/mgr"
|
"golang.org/x/sys/windows/svc/mgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetServiceStatus(name string) (string, error) {
|
|
||||||
conn, err := mgr.Connect()
|
|
||||||
if err != nil {
|
|
||||||
return "n/a", err
|
|
||||||
}
|
|
||||||
defer conn.Disconnect()
|
|
||||||
|
|
||||||
srv, err := conn.OpenService(name)
|
|
||||||
if err != nil {
|
|
||||||
return "n/a", err
|
|
||||||
}
|
|
||||||
defer srv.Close()
|
|
||||||
|
|
||||||
q, err := srv.Query()
|
|
||||||
if err != nil {
|
|
||||||
return "n/a", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return serviceStatusText(uint32(q.State)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Agent) ControlService(name, action string) rmm.WinSvcResp {
|
func (a *Agent) ControlService(name, action string) rmm.WinSvcResp {
|
||||||
conn, err := mgr.Connect()
|
conn, err := mgr.Connect()
|
||||||
|
|
@ -266,27 +247,7 @@ func serviceExists(name string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://docs.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicecontrollerstatus?view=dotnet-plat-ext-3.1
|
|
||||||
func serviceStatusText(num uint32) string {
|
|
||||||
switch num {
|
|
||||||
case 1:
|
|
||||||
return "stopped"
|
|
||||||
case 2:
|
|
||||||
return "start_pending"
|
|
||||||
case 3:
|
|
||||||
return "stop_pending"
|
|
||||||
case 4:
|
|
||||||
return "running"
|
|
||||||
case 5:
|
|
||||||
return "continue_pending"
|
|
||||||
case 6:
|
|
||||||
return "pause_pending"
|
|
||||||
case 7:
|
|
||||||
return "paused"
|
|
||||||
default:
|
|
||||||
return "unknown"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://docs.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicestartmode?view=dotnet-plat-ext-3.1
|
// https://docs.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicestartmode?view=dotnet-plat-ext-3.1
|
||||||
func serviceStartType(num uint32) string {
|
func serviceStartType(num uint32) string {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@ import (
|
||||||
trmm "github.com/wh1te909/trmm-shared"
|
trmm "github.com/wh1te909/trmm-shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SendSoftware() {}
|
|
||||||
|
|
||||||
func GetInstalledSoftware() []trmm.WinSoftwareList { return []trmm.WinSoftwareList{} }
|
func GetInstalledSoftware() []trmm.WinSoftwareList { return []trmm.WinSoftwareList{} }
|
||||||
|
|
||||||
func InstallChoco() {}
|
func InstallChoco() {}
|
||||||
|
|
|
||||||
33
agent/software/software_windows_386.go
Normal file
33
agent/software/software_windows_386.go
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
package software
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
|
wapi "github.com/iamacarpet/go-win64api"
|
||||||
|
trmm "github.com/wh1te909/trmm-shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetInstalledSoftware() []trmm.WinSoftwareList {
|
||||||
|
ret := make([]trmm.WinSoftwareList, 0)
|
||||||
|
|
||||||
|
sw, err := installedSoftwareList()
|
||||||
|
if err != nil {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range sw {
|
||||||
|
t := s.InstallDate
|
||||||
|
ret = append(ret, trmm.WinSoftwareList{
|
||||||
|
Name: utils.CleanString(s.Name()),
|
||||||
|
Version: utils.CleanString(s.Version()),
|
||||||
|
Publisher: utils.CleanString(s.Publisher),
|
||||||
|
InstallDate: fmt.Sprintf("%02d-%d-%02d", t.Year(), t.Month(), t.Day()),
|
||||||
|
Size: utils.ByteCountSI(s.EstimatedSize * 1024),
|
||||||
|
Source: utils.CleanString(s.InstallSource),
|
||||||
|
Location: utils.CleanString(s.InstallLocation),
|
||||||
|
Uninstall: utils.CleanString(s.UninstallString),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
33
agent/software/software_windows_amd64.go
Normal file
33
agent/software/software_windows_amd64.go
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
package software
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
|
wapi "github.com/iamacarpet/go-win64api"
|
||||||
|
trmm "github.com/wh1te909/trmm-shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetInstalledSoftware() []trmm.WinSoftwareList {
|
||||||
|
ret := make([]trmm.WinSoftwareList, 0)
|
||||||
|
|
||||||
|
sw, err := wapi.InstalledSoftwareList()
|
||||||
|
if err != nil {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range sw {
|
||||||
|
t := s.InstallDate
|
||||||
|
ret = append(ret, trmm.WinSoftwareList{
|
||||||
|
Name: utils.CleanString(s.Name()),
|
||||||
|
Version: utils.CleanString(s.Version()),
|
||||||
|
Publisher: utils.CleanString(s.Publisher),
|
||||||
|
InstallDate: fmt.Sprintf("%02d-%d-%02d", t.Year(), t.Month(), t.Day()),
|
||||||
|
Size: utils.ByteCountSI(s.EstimatedSize * 1024),
|
||||||
|
Source: utils.CleanString(s.InstallSource),
|
||||||
|
Location: utils.CleanString(s.InstallLocation),
|
||||||
|
Uninstall: utils.CleanString(s.UninstallString),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
@ -3,11 +3,16 @@ package system
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amidaware/rmmagent/agent/utils"
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
|
ps "github.com/elastic/go-sysinfo"
|
||||||
gocmd "github.com/go-cmd/cmd"
|
gocmd "github.com/go-cmd/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -17,9 +22,16 @@ type CmdStatus struct {
|
||||||
Stderr string
|
Stderr string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewCMDOpts() *CmdOptions {
|
||||||
|
return &CmdOptions{
|
||||||
|
Shell: "/bin/bash",
|
||||||
|
Timeout: 30,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func CmdV2(c *CmdOptions) CmdStatus {
|
func CmdV2(c *CmdOptions) CmdStatus {
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), c.Timeout * time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), c.Timeout*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// Disable output buffering, enable streaming
|
// Disable output buffering, enable streaming
|
||||||
|
|
@ -85,7 +97,7 @@ func CmdV2(c *CmdOptions) CmdStatus {
|
||||||
return
|
return
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
pid := envCmd.Status().PID
|
pid := envCmd.Status().PID
|
||||||
KillProc(int32(pid))
|
utils.KillProc(int32(pid))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -99,3 +111,88 @@ func CmdV2(c *CmdOptions) CmdStatus {
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RunPythonCode(code string, timeout int, args []string) (string, error) {
|
||||||
|
content := []byte(code)
|
||||||
|
dir, err := ioutil.TempDir("", "tacticalpy")
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
tmpfn, _ := ioutil.TempFile(dir, "*.py")
|
||||||
|
if _, err := tmpfn.Write(content); err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tmpfn.Close(); err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
var outb, errb bytes.Buffer
|
||||||
|
cmdArgs := []string{tmpfn.Name()}
|
||||||
|
if len(args) > 0 {
|
||||||
|
cmdArgs = append(cmdArgs, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
//a.Logger.Debugln(cmdArgs)
|
||||||
|
cmd := exec.CommandContext(ctx, GetPythonBin(), cmdArgs...)
|
||||||
|
cmd.Stdout = &outb
|
||||||
|
cmd.Stderr = &errb
|
||||||
|
|
||||||
|
cmdErr := cmd.Run()
|
||||||
|
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
//a.Logger.Debugln("RunPythonCode:", ctx.Err())
|
||||||
|
return "", ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmdErr != nil {
|
||||||
|
//a.Logger.Debugln("RunPythonCode:", cmdErr)
|
||||||
|
return "", cmdErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if errb.String() != "" {
|
||||||
|
//a.Logger.Debugln(errb.String())
|
||||||
|
return errb.String(), errors.New("RunPythonCode stderr")
|
||||||
|
}
|
||||||
|
|
||||||
|
return outb.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHostname() string {
|
||||||
|
host, _ := ps.Host()
|
||||||
|
info := host.Info()
|
||||||
|
return info.Hostname
|
||||||
|
}
|
||||||
|
|
||||||
|
// TotalRAM returns total RAM in GB
|
||||||
|
func TotalRAM() float64 {
|
||||||
|
host, err := ps.Host()
|
||||||
|
if err != nil {
|
||||||
|
return 8.0
|
||||||
|
}
|
||||||
|
|
||||||
|
mem, err := host.Memory()
|
||||||
|
if err != nil {
|
||||||
|
return 8.0
|
||||||
|
}
|
||||||
|
|
||||||
|
return math.Ceil(float64(mem.Total) / 1073741824.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BootTime returns system boot time as a unix timestamp
|
||||||
|
func BootTime() int64 {
|
||||||
|
host, err := ps.Host()
|
||||||
|
if err != nil {
|
||||||
|
return 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
info := host.Info()
|
||||||
|
return info.BootTime.Unix()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,6 @@ import (
|
||||||
trmm "github.com/wh1te909/trmm-shared"
|
trmm "github.com/wh1te909/trmm-shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewCMDOpts() *CmdOptions {
|
|
||||||
return &CmdOptions{
|
|
||||||
Shell: "/bin/bash",
|
|
||||||
Timeout: 30,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetDetached() *syscall.SysProcAttr {
|
func SetDetached() *syscall.SysProcAttr {
|
||||||
return &syscall.SysProcAttr{Setpgid: true}
|
return &syscall.SysProcAttr{Setpgid: true}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,43 @@
|
||||||
package system
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
|
"github.com/amidaware/taskmaster"
|
||||||
|
ps "github.com/elastic/go-sysinfo"
|
||||||
|
"github.com/go-ole/go-ole"
|
||||||
|
"github.com/go-ole/go-ole/oleutil"
|
||||||
|
wapf "github.com/wh1te909/go-win64api"
|
||||||
|
trmm "github.com/wh1te909/trmm-shared"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
"golang.org/x/sys/windows/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProgFilesName = "TacticalAgent"
|
||||||
|
winExeName = "tacticalrmm.exe"
|
||||||
|
)
|
||||||
|
|
||||||
func RunScript(code string, shell string, args []string, timeout int) (stdout, stderr string, exitcode int, e error) {
|
func RunScript(code string, shell string, args []string, timeout int) (stdout, stderr string, exitcode int, e error) {
|
||||||
|
|
||||||
content := []byte(code)
|
content := []byte(code)
|
||||||
|
|
||||||
dir := filepath.Join(os.TempDir(), "trmm")
|
dir := filepath.Join(os.TempDir(), "trmm")
|
||||||
if !trmm.FileExists(dir) {
|
if !trmm.FileExists(dir) {
|
||||||
a.CreateTRMMTempDir()
|
utils.CreateTRMMTempDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultExitCode = 1
|
const defaultExitCode = 1
|
||||||
|
|
||||||
var (
|
var (
|
||||||
outb bytes.Buffer
|
outb bytes.Buffer
|
||||||
errb bytes.Buffer
|
errb bytes.Buffer
|
||||||
|
|
@ -30,17 +57,18 @@ func RunScript(code string, shell string, args []string, timeout int) (stdout, s
|
||||||
|
|
||||||
tmpfn, err := ioutil.TempFile(dir, ext)
|
tmpfn, err := ioutil.TempFile(dir, ext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.Logger.Errorln(err)
|
//a.Logger.Errorln(err)
|
||||||
return "", err.Error(), 85, err
|
return "", err.Error(), 85, err
|
||||||
}
|
}
|
||||||
defer os.Remove(tmpfn.Name())
|
|
||||||
|
|
||||||
|
defer os.Remove(tmpfn.Name())
|
||||||
if _, err := tmpfn.Write(content); err != nil {
|
if _, err := tmpfn.Write(content); err != nil {
|
||||||
a.Logger.Errorln(err)
|
//a.Logger.Errorln(err)
|
||||||
return "", err.Error(), 85, err
|
return "", err.Error(), 85, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tmpfn.Close(); err != nil {
|
if err := tmpfn.Close(); err != nil {
|
||||||
a.Logger.Errorln(err)
|
//a.Logger.Errorln(err)
|
||||||
return "", err.Error(), 85, err
|
return "", err.Error(), 85, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,7 +77,7 @@ func RunScript(code string, shell string, args []string, timeout int) (stdout, s
|
||||||
exe = "Powershell"
|
exe = "Powershell"
|
||||||
cmdArgs = []string{"-NonInteractive", "-NoProfile", "-ExecutionPolicy", "Bypass", tmpfn.Name()}
|
cmdArgs = []string{"-NonInteractive", "-NoProfile", "-ExecutionPolicy", "Bypass", tmpfn.Name()}
|
||||||
case "python":
|
case "python":
|
||||||
exe = a.PyBin
|
exe = GetPythonBin()
|
||||||
cmdArgs = []string{tmpfn.Name()}
|
cmdArgs = []string{tmpfn.Name()}
|
||||||
case "cmd":
|
case "cmd":
|
||||||
exe = tmpfn.Name()
|
exe = tmpfn.Name()
|
||||||
|
|
@ -61,16 +89,15 @@ func RunScript(code string, shell string, args []string, timeout int) (stdout, s
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
var timedOut bool = false
|
var timedOut bool = false
|
||||||
cmd := exec.Command(exe, cmdArgs...)
|
cmd := exec.Command(exe, cmdArgs...)
|
||||||
cmd.Stdout = &outb
|
cmd.Stdout = &outb
|
||||||
cmd.Stderr = &errb
|
cmd.Stderr = &errb
|
||||||
|
|
||||||
if cmdErr := cmd.Start(); cmdErr != nil {
|
if cmdErr := cmd.Start(); cmdErr != nil {
|
||||||
a.Logger.Debugln(cmdErr)
|
//a.Logger.Debugln(cmdErr)
|
||||||
return "", cmdErr.Error(), 65, cmdErr
|
return "", cmdErr.Error(), 65, cmdErr
|
||||||
}
|
}
|
||||||
|
|
||||||
pid := int32(cmd.Process.Pid)
|
pid := int32(cmd.Process.Pid)
|
||||||
|
|
||||||
// custom context handling, we need to kill child procs if this is a batch script,
|
// custom context handling, we need to kill child procs if this is a batch script,
|
||||||
|
|
@ -80,20 +107,19 @@ func RunScript(code string, shell string, args []string, timeout int) (stdout, s
|
||||||
|
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
|
||||||
_ = KillProc(p)
|
_ = utils.KillProc(p)
|
||||||
timedOut = true
|
timedOut = true
|
||||||
}(pid)
|
}(pid)
|
||||||
|
|
||||||
cmdErr := cmd.Wait()
|
cmdErr := cmd.Wait()
|
||||||
|
|
||||||
if timedOut {
|
if timedOut {
|
||||||
stdout = CleanString(outb.String())
|
stdout = utils.CleanString(outb.String())
|
||||||
stderr = fmt.Sprintf("%s\nScript timed out after %d seconds", CleanString(errb.String()), timeout)
|
stderr = fmt.Sprintf("%s\nScript timed out after %d seconds", utils.CleanString(errb.String()), timeout)
|
||||||
exitcode = 98
|
exitcode = 98
|
||||||
a.Logger.Debugln("Script check timeout:", ctx.Err())
|
//a.Logger.Debugln("Script check timeout:", ctx.Err())
|
||||||
} else {
|
} else {
|
||||||
stdout = CleanString(outb.String())
|
stdout = utils.CleanString(outb.String())
|
||||||
stderr = CleanString(errb.String())
|
stderr = utils.CleanString(errb.String())
|
||||||
|
|
||||||
// get the exit code
|
// get the exit code
|
||||||
if cmdErr != nil {
|
if cmdErr != nil {
|
||||||
|
|
@ -115,5 +141,371 @@ func RunScript(code string, shell string, args []string, timeout int) (stdout, s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return stdout, stderr, exitcode, nil
|
return stdout, stderr, exitcode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CMD(exe string, args []string, timeout int, detached bool) (output [2]string, e error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var outb, errb bytes.Buffer
|
||||||
|
cmd := exec.CommandContext(ctx, exe, args...)
|
||||||
|
if detached {
|
||||||
|
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||||
|
CreationFlags: windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd.Stdout = &outb
|
||||||
|
cmd.Stderr = &errb
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return [2]string{"", ""},
|
||||||
|
fmt.Errorf("%s: %s", err, utils.CleanString(errb.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
return [2]string{"", ""}, ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
return [2]string{
|
||||||
|
utils.CleanString(outb.String()),
|
||||||
|
utils.CleanString(errb.String()),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetDetached() *syscall.SysProcAttr {
|
||||||
|
return &syscall.SysProcAttr{
|
||||||
|
CreationFlags: windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CMDShell(shell string, cmdArgs []string, command string, timeout int, detached bool) (output [2]string, e error) {
|
||||||
|
var (
|
||||||
|
outb bytes.Buffer
|
||||||
|
errb bytes.Buffer
|
||||||
|
cmd *exec.Cmd
|
||||||
|
timedOut = false
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if len(cmdArgs) > 0 && command == "" {
|
||||||
|
switch shell {
|
||||||
|
case "cmd":
|
||||||
|
cmdArgs = append([]string{"/C"}, cmdArgs...)
|
||||||
|
cmd = exec.Command("cmd.exe", cmdArgs...)
|
||||||
|
case "powershell":
|
||||||
|
cmdArgs = append([]string{"-NonInteractive", "-NoProfile"}, cmdArgs...)
|
||||||
|
cmd = exec.Command("powershell.exe", cmdArgs...)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch shell {
|
||||||
|
case "cmd":
|
||||||
|
cmd = exec.Command("cmd.exe")
|
||||||
|
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||||
|
CmdLine: fmt.Sprintf("cmd.exe /C %s", command),
|
||||||
|
}
|
||||||
|
case "powershell":
|
||||||
|
cmd = exec.Command("Powershell", "-NonInteractive", "-NoProfile", command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
|
||||||
|
if detached {
|
||||||
|
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||||
|
CreationFlags: windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd.Stdout = &outb
|
||||||
|
cmd.Stderr = &errb
|
||||||
|
cmd.Start()
|
||||||
|
|
||||||
|
pid := int32(cmd.Process.Pid)
|
||||||
|
|
||||||
|
go func(p int32) {
|
||||||
|
|
||||||
|
<-ctx.Done()
|
||||||
|
|
||||||
|
_ = utils.KillProc(p)
|
||||||
|
timedOut = true
|
||||||
|
}(pid)
|
||||||
|
|
||||||
|
err := cmd.Wait()
|
||||||
|
|
||||||
|
if timedOut {
|
||||||
|
return [2]string{
|
||||||
|
utils.CleanString(outb.String()),
|
||||||
|
utils.CleanString(errb.String())},
|
||||||
|
ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return [2]string{
|
||||||
|
utils.CleanString(outb.String()),
|
||||||
|
utils.CleanString(errb.String())},
|
||||||
|
err
|
||||||
|
}
|
||||||
|
|
||||||
|
return [2]string{
|
||||||
|
utils.CleanString(outb.String()),
|
||||||
|
utils.CleanString(errb.String())},
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProgramDirectory() string {
|
||||||
|
pd := filepath.Join(os.Getenv("ProgramFiles"), ProgFilesName)
|
||||||
|
return pd
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProgramEXE() string {
|
||||||
|
exe := filepath.Join(GetProgramDirectory(), winExeName)
|
||||||
|
return exe
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPythonBin() string {
|
||||||
|
var pybin string
|
||||||
|
switch runtime.GOARCH {
|
||||||
|
case "amd64":
|
||||||
|
pybin = filepath.Join(GetProgramDirectory(), "py38-x64", "python.exe")
|
||||||
|
case "386":
|
||||||
|
pybin = filepath.Join(GetProgramDirectory(), "py38-x32", "python.exe")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pybin
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoggedOnUser returns the first logged on user it finds
|
||||||
|
func LoggedOnUser() string {
|
||||||
|
pyCode := `
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
try:
|
||||||
|
u = psutil.users()[0].name
|
||||||
|
if u.isascii():
|
||||||
|
print(u, end='')
|
||||||
|
else:
|
||||||
|
print('notascii', end='')
|
||||||
|
except Exception as e:
|
||||||
|
print("None", end='')
|
||||||
|
|
||||||
|
`
|
||||||
|
// try with psutil first, if fails, fallback to golang
|
||||||
|
user, err := RunPythonCode(pyCode, 5, []string{})
|
||||||
|
if err == nil && user != "notascii" {
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
users, err := wapf.ListLoggedInUsers()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln("LoggedOnUser error", err)
|
||||||
|
return "None"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(users) == 0 {
|
||||||
|
return "None"
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, u := range users {
|
||||||
|
// remove the computername or domain
|
||||||
|
return strings.Split(u.FullUser(), `\`)[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return "None"
|
||||||
|
}
|
||||||
|
|
||||||
|
func PlatVer() (string, error) {
|
||||||
|
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.ALL_ACCESS)
|
||||||
|
if err != nil {
|
||||||
|
return "n/a", err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer k.Close()
|
||||||
|
dv, _, err := k.GetStringValue("DisplayVersion")
|
||||||
|
if err == nil {
|
||||||
|
return dv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
relid, _, err := k.GetStringValue("ReleaseId")
|
||||||
|
if err != nil {
|
||||||
|
return "n/a", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return relid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnablePing enables ping
|
||||||
|
func EnablePing() {
|
||||||
|
args := make([]string, 0)
|
||||||
|
cmd := `netsh advfirewall firewall add rule name="ICMP Allow incoming V4 echo request" protocol=icmpv4:8,any dir=in action=allow`
|
||||||
|
_, err := CMDShell("cmd", args, cmd, 10, false)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableRDP enables Remote Desktop
|
||||||
|
func EnableRDP() {
|
||||||
|
k, _, err := registry.CreateKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Control\Terminal Server`, registry.ALL_ACCESS)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
defer k.Close()
|
||||||
|
|
||||||
|
err = k.SetDWordValue("fDenyTSConnections", 0)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]string, 0)
|
||||||
|
cmd := `netsh advfirewall firewall set rule group="remote desktop" new enable=Yes`
|
||||||
|
_, cerr := CMDShell("cmd", args, cmd, 10, false)
|
||||||
|
if cerr != nil {
|
||||||
|
fmt.Println(cerr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableSleepHibernate disables sleep and hibernate
|
||||||
|
func DisableSleepHibernate() {
|
||||||
|
k, _, err := registry.CreateKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Control\Session Manager\Power`, registry.ALL_ACCESS)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
defer k.Close()
|
||||||
|
|
||||||
|
err = k.SetDWordValue("HiberbootEnabled", 0)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]string, 0)
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
currents := []string{"ac", "dc"}
|
||||||
|
for _, i := range currents {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(c string) {
|
||||||
|
defer wg.Done()
|
||||||
|
_, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /set%svalueindex scheme_current sub_buttons lidaction 0", c), 5, false)
|
||||||
|
_, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -standby-timeout-%s 0", c), 5, false)
|
||||||
|
_, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -hibernate-timeout-%s 0", c), 5, false)
|
||||||
|
_, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -disk-timeout-%s 0", c), 5, false)
|
||||||
|
_, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -monitor-timeout-%s 0", c), 5, false)
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
_, _ = CMDShell("cmd", args, "powercfg -S SCHEME_CURRENT", 5, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCOMObject creates a new COM object for the specifed ProgramID.
|
||||||
|
func NewCOMObject(id string) (*ole.IDispatch, error) {
|
||||||
|
unknown, err := oleutil.CreateObject(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create initial unknown object: %v", err)
|
||||||
|
}
|
||||||
|
defer unknown.Release()
|
||||||
|
|
||||||
|
obj, err := unknown.QueryInterface(ole.IID_IDispatch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create query interface: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemRebootRequired checks whether a system reboot is required.
|
||||||
|
func SystemRebootRequired() (bool, error) {
|
||||||
|
regKeys := []string{
|
||||||
|
`SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired`,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range regKeys {
|
||||||
|
k, err := registry.OpenKey(registry.LOCAL_MACHINE, key, registry.QUERY_VALUE)
|
||||||
|
if err == nil {
|
||||||
|
k.Close()
|
||||||
|
return true, nil
|
||||||
|
} else if err != registry.ErrNotExist {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanupSchedTasks removes all tacticalrmm sched tasks during uninstall
|
||||||
|
func CleanupSchedTasks() {
|
||||||
|
conn, err := taskmaster.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Disconnect()
|
||||||
|
|
||||||
|
tasks, err := conn.GetRegisteredTasks()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, task := range tasks {
|
||||||
|
if strings.HasPrefix(task.Name, "TacticalRMM_") {
|
||||||
|
conn.DeleteTask(fmt.Sprintf("\\%s", task.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.Release()
|
||||||
|
}
|
||||||
|
|
||||||
|
func KillHungUpdates() {
|
||||||
|
procs, err := ps.Processes()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, process := range procs {
|
||||||
|
p, err := process.Info()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.Contains(p.Exe, "winagent-v") {
|
||||||
|
//a.Logger.Debugln("killing process", p.Exe)
|
||||||
|
utils.KillProc(int32(p.PID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OsString() string {
|
||||||
|
host, _ := ps.Host()
|
||||||
|
info := host.Info()
|
||||||
|
osInf := info.OS
|
||||||
|
|
||||||
|
var arch string
|
||||||
|
switch info.Architecture {
|
||||||
|
case "x86_64":
|
||||||
|
arch = "64 bit"
|
||||||
|
case "x86":
|
||||||
|
arch = "32 bit"
|
||||||
|
}
|
||||||
|
|
||||||
|
var osFullName string
|
||||||
|
platver, err := PlatVer()
|
||||||
|
if err != nil {
|
||||||
|
osFullName = fmt.Sprintf("%s, %s (build %s)", osInf.Name, arch, osInf.Build)
|
||||||
|
} else {
|
||||||
|
osFullName = fmt.Sprintf("%s, %s v%s (build %s)", osInf.Name, arch, platver, osInf.Build)
|
||||||
|
}
|
||||||
|
|
||||||
|
return osFullName
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddDefenderExlusions() {
|
||||||
|
code := `
|
||||||
|
Add-MpPreference -ExclusionPath 'C:\Program Files\TacticalAgent\*'
|
||||||
|
Add-MpPreference -ExclusionPath 'C:\Windows\Temp\winagent-v*.exe'
|
||||||
|
Add-MpPreference -ExclusionPath 'C:\Windows\Temp\trmm\*'
|
||||||
|
Add-MpPreference -ExclusionPath 'C:\Program Files\Mesh Agent\*'
|
||||||
|
`
|
||||||
|
_, _, _, err := RunScript(code, "powershell", []string{}, 20)
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
180
agent/tactical/checks/checks_windows.go
Normal file
180
agent/tactical/checks/checks_windows.go
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
package checks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
|
rmm "github.com/amidaware/rmmagent/shared"
|
||||||
|
ps "github.com/elastic/go-sysinfo"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckRunner(agentID string) {
|
||||||
|
sleepDelay := utils.RandRange(14, 22)
|
||||||
|
//a.Logger.Debugf("CheckRunner() init sleeping for %v seconds", sleepDelay)
|
||||||
|
time.Sleep(time.Duration(sleepDelay) * time.Second)
|
||||||
|
for {
|
||||||
|
interval, err := GetCheckInterval(agentID)
|
||||||
|
if err == nil && !ChecksRunning() {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
_, err = system.CMD(system.GetProgramEXE(), []string{"-m", "checkrunner"}, 600, false)
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Errorln("Checkrunner RunChecks", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RunChecks(agentID, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//a.Logger.Debugln("Checkrunner sleeping for", interval)
|
||||||
|
time.Sleep(time.Duration(interval) * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCheckInterval(agentID string) (int, error) {
|
||||||
|
r, err := a.rClient.R().SetResult(&rmm.CheckInfo{}).Get(fmt.Sprintf("/api/v3/%s/checkinterval/", a.AgentID))
|
||||||
|
if err != nil {
|
||||||
|
a.Logger.Debugln(err)
|
||||||
|
return 120, err
|
||||||
|
}
|
||||||
|
if r.IsError() {
|
||||||
|
a.Logger.Debugln("Checkinterval response code:", r.StatusCode())
|
||||||
|
return 120, fmt.Errorf("checkinterval response code: %v", r.StatusCode())
|
||||||
|
}
|
||||||
|
interval := r.Result().(*rmm.CheckInfo).Interval
|
||||||
|
return interval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChecksRunning prevents duplicate checks from running
|
||||||
|
// Have to do it this way, can't use atomic because they can run from both rpc and tacticalagent services
|
||||||
|
func ChecksRunning() bool {
|
||||||
|
running := false
|
||||||
|
procs, err := ps.Processes()
|
||||||
|
if err != nil {
|
||||||
|
return running
|
||||||
|
}
|
||||||
|
Out:
|
||||||
|
for _, process := range procs {
|
||||||
|
p, err := process.Info()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p.PID == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p.Exe != system.GetProgramEXE() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arg := range p.Args {
|
||||||
|
if arg == "runchecks" || arg == "checkrunner" {
|
||||||
|
running = true
|
||||||
|
break Out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return running
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunChecks(agentID string, force bool) error {
|
||||||
|
data := rmm.AllChecks{}
|
||||||
|
var url string
|
||||||
|
if force {
|
||||||
|
url = fmt.Sprintf("/api/v3/%s/runchecks/", agentID)
|
||||||
|
} else {
|
||||||
|
url = fmt.Sprintf("/api/v3/%s/checkrunner/", agentID)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := a.rClient.R().Get(url)
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.IsError() {
|
||||||
|
//a.Logger.Debugln("Checkrunner response code:", r.StatusCode())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(r.Body(), &data); err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
eventLogChecks := make([]rmm.Check, 0)
|
||||||
|
winServiceChecks := make([]rmm.Check, 0)
|
||||||
|
for _, check := range data.Checks {
|
||||||
|
switch check.CheckType {
|
||||||
|
case "diskspace":
|
||||||
|
wg.Add(1)
|
||||||
|
go func(c rmm.Check, wg *sync.WaitGroup, r *resty.Client) {
|
||||||
|
defer wg.Done()
|
||||||
|
utils.RandomCheckDelay()
|
||||||
|
a.SendDiskCheckResult(a.DiskCheck(c), r)
|
||||||
|
}(check, &wg, a.rClient)
|
||||||
|
case "cpuload":
|
||||||
|
wg.Add(1)
|
||||||
|
go func(c rmm.Check, wg *sync.WaitGroup, r *resty.Client) {
|
||||||
|
defer wg.Done()
|
||||||
|
a.CPULoadCheck(c, r)
|
||||||
|
}(check, &wg, a.rClient)
|
||||||
|
case "memory":
|
||||||
|
wg.Add(1)
|
||||||
|
go func(c rmm.Check, wg *sync.WaitGroup, r *resty.Client) {
|
||||||
|
defer wg.Done()
|
||||||
|
randomCheckDelay()
|
||||||
|
a.MemCheck(c, r)
|
||||||
|
}(check, &wg, a.rClient)
|
||||||
|
case "ping":
|
||||||
|
wg.Add(1)
|
||||||
|
go func(c rmm.Check, wg *sync.WaitGroup, r *resty.Client) {
|
||||||
|
defer wg.Done()
|
||||||
|
randomCheckDelay()
|
||||||
|
a.SendPingCheckResult(a.PingCheck(c), r)
|
||||||
|
}(check, &wg, a.rClient)
|
||||||
|
case "script":
|
||||||
|
wg.Add(1)
|
||||||
|
go func(c rmm.Check, wg *sync.WaitGroup, r *resty.Client) {
|
||||||
|
defer wg.Done()
|
||||||
|
randomCheckDelay()
|
||||||
|
a.ScriptCheck(c, r)
|
||||||
|
}(check, &wg, a.rClient)
|
||||||
|
case "winsvc":
|
||||||
|
winServiceChecks = append(winServiceChecks, check)
|
||||||
|
case "eventlog":
|
||||||
|
eventLogChecks = append(eventLogChecks, check)
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(winServiceChecks) > 0 {
|
||||||
|
wg.Add(len(winServiceChecks))
|
||||||
|
go func(wg *sync.WaitGroup, r *resty.Client) {
|
||||||
|
for _, winSvcCheck := range winServiceChecks {
|
||||||
|
defer wg.Done()
|
||||||
|
a.SendWinSvcCheckResult(a.WinSvcCheck(winSvcCheck), r)
|
||||||
|
}
|
||||||
|
}(&wg, a.rClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(eventLogChecks) > 0 {
|
||||||
|
wg.Add(len(eventLogChecks))
|
||||||
|
go func(wg *sync.WaitGroup, r *resty.Client) {
|
||||||
|
for _, evtCheck := range eventLogChecks {
|
||||||
|
defer wg.Done()
|
||||||
|
a.EventLogCheck(evtCheck, r)
|
||||||
|
}
|
||||||
|
}(&wg, a.rClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
179
agent/tactical/service/service.go
Normal file
179
agent/tactical/service/service.go
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/disk"
|
||||||
|
"github.com/amidaware/rmmagent/agent/services"
|
||||||
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/checks"
|
||||||
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
|
"github.com/amidaware/rmmagent/agent/wmi"
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
|
trmm "github.com/wh1te909/trmm-shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
var natsCheckin = []string{"agent-hello", "agent-agentinfo", "agent-disks", "agent-winsvc", "agent-publicip", "agent-wmi"}
|
||||||
|
|
||||||
|
func RunAsService(agentID string, version string) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
go AgentSvc(version)
|
||||||
|
go checks.CheckRunner(agentID)
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AgentSvc(version string) {
|
||||||
|
config := tactical.NewAgentConfig()
|
||||||
|
go tactical.GetPython(false)
|
||||||
|
utils.CreateTRMMTempDir()
|
||||||
|
tactical.RunMigrations()
|
||||||
|
|
||||||
|
sleepDelay := utils.RandRange(14, 22)
|
||||||
|
//a.Logger.Debugf("AgentSvc() sleeping for %v seconds", sleepDelay)
|
||||||
|
time.Sleep(time.Duration(sleepDelay) * time.Second)
|
||||||
|
|
||||||
|
opts := SetupNatsOptions(config.AgentID, config.Token)
|
||||||
|
server := fmt.Sprintf("tls://%s:4222", config.APIURL)
|
||||||
|
nc, err := nats.Connect(server, opts...)
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Fatalln("AgentSvc() nats.Connect()", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range natsCheckin {
|
||||||
|
NatsMessage(config.AgentID, version, nc, s)
|
||||||
|
time.Sleep(time.Duration(utils.RandRange(100, 400)) * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
go tactical.SyncMeshNodeID()
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(utils.RandRange(1, 3)) * time.Second)
|
||||||
|
AgentStartup(config.AgentID)
|
||||||
|
tactical.SendSoftware()
|
||||||
|
|
||||||
|
checkInHelloTicker := time.NewTicker(time.Duration(utils.RandRange(30, 60)) * time.Second)
|
||||||
|
checkInAgentInfoTicker := time.NewTicker(time.Duration(utils.RandRange(200, 400)) * time.Second)
|
||||||
|
checkInWinSvcTicker := time.NewTicker(time.Duration(utils.RandRange(2400, 3000)) * time.Second)
|
||||||
|
checkInPubIPTicker := time.NewTicker(time.Duration(utils.RandRange(300, 500)) * time.Second)
|
||||||
|
checkInDisksTicker := time.NewTicker(time.Duration(utils.RandRange(1000, 2000)) * time.Second)
|
||||||
|
checkInSWTicker := time.NewTicker(time.Duration(utils.RandRange(2800, 3500)) * time.Second)
|
||||||
|
checkInWMITicker := time.NewTicker(time.Duration(utils.RandRange(3000, 4000)) * time.Second)
|
||||||
|
syncMeshTicker := time.NewTicker(time.Duration(utils.RandRange(800, 1200)) * time.Second)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-checkInHelloTicker.C:
|
||||||
|
NatsMessage(config.AgentID, version, nc, "agent-hello")
|
||||||
|
case <-checkInAgentInfoTicker.C:
|
||||||
|
NatsMessage(config.AgentID, version, nc, "agent-agentinfo")
|
||||||
|
case <-checkInWinSvcTicker.C:
|
||||||
|
NatsMessage(config.AgentID, version, nc, "agent-winsvc")
|
||||||
|
case <-checkInPubIPTicker.C:
|
||||||
|
NatsMessage(config.AgentID, version, nc, "agent-publicip")
|
||||||
|
case <-checkInDisksTicker.C:
|
||||||
|
NatsMessage(config.AgentID, version, nc, "agent-disks")
|
||||||
|
case <-checkInSWTicker.C:
|
||||||
|
tactical.SendSoftware()
|
||||||
|
case <-checkInWMITicker.C:
|
||||||
|
NatsMessage(config.AgentID, version, nc, "agent-wmi")
|
||||||
|
case <-syncMeshTicker.C:
|
||||||
|
tactical.SyncMeshNodeID()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetupNatsOptions(agentID string, token string) []nats.Option {
|
||||||
|
opts := make([]nats.Option, 0)
|
||||||
|
opts = append(opts, nats.Name("TacticalRMM"))
|
||||||
|
opts = append(opts, nats.UserInfo(agentID, token))
|
||||||
|
opts = append(opts, nats.ReconnectWait(time.Second*5))
|
||||||
|
opts = append(opts, nats.RetryOnFailedConnect(true))
|
||||||
|
opts = append(opts, nats.MaxReconnects(-1))
|
||||||
|
opts = append(opts, nats.ReconnectBufSize(-1))
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func NatsMessage(agentID string, version string, nc *nats.Conn, mode string) {
|
||||||
|
var resp []byte
|
||||||
|
var payload interface{}
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case "agent-hello":
|
||||||
|
payload = trmm.CheckInNats{
|
||||||
|
Agentid: agentID,
|
||||||
|
Version: version,
|
||||||
|
}
|
||||||
|
case "agent-winsvc":
|
||||||
|
payload = trmm.WinSvcNats{
|
||||||
|
Agentid: agentID,
|
||||||
|
WinSvcs: services.GetServices(),
|
||||||
|
}
|
||||||
|
case "agent-agentinfo":
|
||||||
|
osinfo := system.OsString()
|
||||||
|
reboot, err := system.SystemRebootRequired()
|
||||||
|
if err != nil {
|
||||||
|
reboot = false
|
||||||
|
}
|
||||||
|
payload = trmm.AgentInfoNats{
|
||||||
|
Agentid: agentID,
|
||||||
|
Username: system.LoggedOnUser(),
|
||||||
|
Hostname: system.GetHostname(),
|
||||||
|
OS: osinfo,
|
||||||
|
Platform: runtime.GOOS,
|
||||||
|
TotalRAM: system.TotalRAM(),
|
||||||
|
BootTime: system.BootTime(),
|
||||||
|
RebootNeeded: reboot,
|
||||||
|
GoArch: runtime.GOARCH,
|
||||||
|
}
|
||||||
|
case "agent-wmi":
|
||||||
|
payload = trmm.WinWMINats{
|
||||||
|
Agentid: agentID,
|
||||||
|
WMI: wmi.GetWMIInfo(),
|
||||||
|
}
|
||||||
|
case "agent-disks":
|
||||||
|
payload = trmm.WinDisksNats{
|
||||||
|
Agentid: agentID,
|
||||||
|
Disks: disk.GetDisks(),
|
||||||
|
}
|
||||||
|
case "agent-publicip":
|
||||||
|
payload = trmm.PublicIPNats{
|
||||||
|
Agentid: agentID,
|
||||||
|
PublicIP: a.PublicIP(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//a.Logger.Debugln(mode, payload)
|
||||||
|
ret.Encode(payload)
|
||||||
|
nc.PublishRequest(a.AgentID, mode, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DoNatsCheckIn() {
|
||||||
|
opts := SetupNatsOptions()
|
||||||
|
server := fmt.Sprintf("tls://%s:4222", a.ApiURL)
|
||||||
|
nc, err := nats.Connect(server, opts...)
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Errorln(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range natsCheckin {
|
||||||
|
time.Sleep(time.Duration(utils.RandRange(100, 400)) * time.Millisecond)
|
||||||
|
NatsMessage(nc, s)
|
||||||
|
}
|
||||||
|
nc.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AgentStartup(agentID string) {
|
||||||
|
url := "/api/v3/checkin/"
|
||||||
|
payload := map[string]interface{}{"agent_id": agentID}
|
||||||
|
_, err := tactical.PostRequest(url, payload, 15)
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln("AgentStartup()", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,25 @@ import (
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func PostRequest(url string, body interface{}, timeout time.Duration) (resty.Response, error) {
|
||||||
|
agentConfig := NewAgentConfig()
|
||||||
|
client := resty.New()
|
||||||
|
client.SetBaseURL(agentConfig.BaseURL)
|
||||||
|
client.SetTimeout(timeout * time.Second)
|
||||||
|
client.SetCloseConnection(true)
|
||||||
|
if len(agentConfig.Proxy) > 0 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if shared.DEBUG {
|
||||||
|
client.SetDebug(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := client.R().SetBody(body).Post(url)
|
||||||
|
|
||||||
|
return *response, err
|
||||||
|
}
|
||||||
|
|
||||||
func SyncMeshNodeID() bool {
|
func SyncMeshNodeID() bool {
|
||||||
id, err := GetMeshNodeID()
|
id, err := GetMeshNodeID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -16,21 +35,12 @@ func SyncMeshNodeID() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
agentConfig := NewAgentConfig()
|
agentConfig := NewAgentConfig()
|
||||||
|
|
||||||
payload := shared.MeshNodeID{
|
payload := shared.MeshNodeID{
|
||||||
Func: "syncmesh",
|
Func: "syncmesh",
|
||||||
Agentid: agentConfig.AgentID,
|
Agentid: agentConfig.AgentID,
|
||||||
NodeID: utils.StripAll(id),
|
NodeID: utils.StripAll(id),
|
||||||
}
|
}
|
||||||
|
|
||||||
client := resty.New()
|
_, err = PostRequest("/api/v3/syncmesh/", payload, 15)
|
||||||
client.SetBaseURL(agentConfig.BaseURL)
|
|
||||||
client.SetTimeout(15 * time.Second)
|
|
||||||
client.SetCloseConnection(true)
|
|
||||||
if shared.DEBUG {
|
|
||||||
client.SetDebug(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = client.R().SetBody(payload).Post("/api/v3/syncmesh/")
|
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
@ -181,4 +181,6 @@ func RunTask(id int) error { return nil }
|
||||||
|
|
||||||
func installMesh(meshbin, exe, proxy string) (string, error) {
|
func installMesh(meshbin, exe, proxy string) (string, error) {
|
||||||
return "not implemented", nil
|
return "not implemented", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SendSoftware() {}
|
||||||
|
|
@ -1,5 +1,41 @@
|
||||||
package tactical
|
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/utils"
|
||||||
|
"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 {
|
func NewAgentConfig() *rmm.AgentConfig {
|
||||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`, registry.ALL_ACCESS)
|
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`, registry.ALL_ACCESS)
|
||||||
|
|
||||||
|
|
@ -28,4 +64,273 @@ func NewAgentConfig() *rmm.AgentConfig {
|
||||||
Proxy: proxy,
|
Proxy: proxy,
|
||||||
CustomMeshDir: customMeshDir,
|
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()
|
||||||
|
system.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()
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
2
agent/tasks/structs.go
Normal file
2
agent/tasks/structs.go
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
package tasks
|
||||||
|
|
||||||
44
agent/tasks/structs_windows.go
Normal file
44
agent/tasks/structs_windows.go
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
package tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/amidaware/taskmaster"
|
||||||
|
"github.com/rickb777/date/period"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SchedTask struct {
|
||||||
|
PK int `json:"pk"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Trigger string `json:"trigger"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
DayInterval taskmaster.DayInterval `json:"day_interval"`
|
||||||
|
WeekInterval taskmaster.WeekInterval `json:"week_interval"`
|
||||||
|
DaysOfWeek taskmaster.DayOfWeek `json:"days_of_week"`
|
||||||
|
DaysOfMonth taskmaster.DayOfMonth `json:"days_of_month"`
|
||||||
|
RunOnLastDayOfMonth bool `json:"run_on_last_day_of_month"`
|
||||||
|
MonthsOfYear taskmaster.Month `json:"months_of_year"`
|
||||||
|
WeeksOfMonth taskmaster.Week `json:"weeks_of_month"`
|
||||||
|
StartYear int `json:"start_year"`
|
||||||
|
StartMonth time.Month `json:"start_month"`
|
||||||
|
StartDay int `json:"start_day"`
|
||||||
|
StartHour int `json:"start_hour"`
|
||||||
|
StartMinute int `json:"start_min"`
|
||||||
|
ExpireYear int `json:"expire_year"`
|
||||||
|
ExpireMonth time.Month `json:"expire_month"`
|
||||||
|
ExpireDay int `json:"expire_day"`
|
||||||
|
ExpireHour int `json:"expire_hour"`
|
||||||
|
ExpireMinute int `json:"expire_min"`
|
||||||
|
RandomDelay period.Period `json:"random_delay"`
|
||||||
|
RepetitionInterval period.Period `json:"repetition_interval"`
|
||||||
|
RepetitionDuration period.Period `json:"repetition_duration"`
|
||||||
|
StopAtDurationEnd bool `json:"stop_at_duration_end"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
WorkDir string `json:"workdir"`
|
||||||
|
Args string `json:"args"`
|
||||||
|
TaskPolicy taskmaster.TaskInstancesPolicy `json:"multiple_instances"`
|
||||||
|
RunASAPAfterMissed bool `json:"start_when_available"`
|
||||||
|
DeleteAfter bool `json:"delete_expired_task_after"`
|
||||||
|
Overwrite bool `json:"overwrite_task"`
|
||||||
|
}
|
||||||
2
agent/tasks/tasks_windows.go
Normal file
2
agent/tasks/tasks_windows.go
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
package tasks
|
||||||
|
|
||||||
|
|
@ -311,26 +311,7 @@ func DeleteSchedTask(name string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanupSchedTasks removes all tacticalrmm sched tasks during uninstall
|
|
||||||
func CleanupSchedTasks() {
|
|
||||||
conn, err := taskmaster.Connect()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer conn.Disconnect()
|
|
||||||
|
|
||||||
tasks, err := conn.GetRegisteredTasks()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, task := range tasks {
|
|
||||||
if strings.HasPrefix(task.Name, "TacticalRMM_") {
|
|
||||||
conn.DeleteTask(fmt.Sprintf("\\%s", task.Name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tasks.Release()
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListSchedTasks() []string {
|
func ListSchedTasks() []string {
|
||||||
ret := make([]string, 0)
|
ret := make([]string, 0)
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,20 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/zip"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amidaware/rmmagent/shared"
|
"github.com/amidaware/rmmagent/shared"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/shirou/gopsutil/v3/process"
|
||||||
|
trmm "github.com/wh1te909/trmm-shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CaptureOutput(f func()) string {
|
func CaptureOutput(f func()) string {
|
||||||
|
|
@ -77,7 +82,7 @@ func WebRequest(requestType string, timeout time.Duration, payload map[string]st
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := client.R().Get(url)
|
result, err := client.R().Get(url)
|
||||||
return *result, err
|
return *result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// StripAll strips all whitespace and newline chars
|
// StripAll strips all whitespace and newline chars
|
||||||
|
|
@ -86,4 +91,96 @@ func StripAll(s string) string {
|
||||||
s = strings.Trim(s, "\n")
|
s = strings.Trim(s, "\n")
|
||||||
s = strings.Trim(s, "\r")
|
s = strings.Trim(s, "\r")
|
||||||
return s
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// KillProc kills a process and its children
|
||||||
|
func KillProc(pid int32) error {
|
||||||
|
p, err := process.NewProcess(pid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
children, err := p.Children()
|
||||||
|
if err == nil {
|
||||||
|
for _, child := range children {
|
||||||
|
if err := child.Kill(); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.Kill(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateTRMMTempDir() {
|
||||||
|
// create the temp dir for running scripts
|
||||||
|
dir := filepath.Join(os.TempDir(), "trmm")
|
||||||
|
if !trmm.FileExists(dir) {
|
||||||
|
err := os.Mkdir(dir, 0775)
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Errorln(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandRange(min, max int) int {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
return rand.Intn(max-min) + min
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://golangcode.com/unzip-files-in-go/
|
||||||
|
func Unzip(src, dest string) error {
|
||||||
|
r, err := zip.OpenReader(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer r.Close()
|
||||||
|
for _, f := range r.File {
|
||||||
|
// Store filename/path for returning and using later on
|
||||||
|
fpath := filepath.Join(dest, f.Name)
|
||||||
|
// Check for ZipSlip. More Info: http://bit.ly/2MsjAWE
|
||||||
|
if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
|
||||||
|
return fmt.Errorf("%s: illegal file path", fpath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.FileInfo().IsDir() {
|
||||||
|
// Make Folder
|
||||||
|
os.MkdirAll(fpath, os.ModePerm)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make File
|
||||||
|
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rc, err := f.Open()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(outFile, rc)
|
||||||
|
// Close the file without defer to close before next iteration of loop
|
||||||
|
outFile.Close()
|
||||||
|
rc.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandomCheckDelay() {
|
||||||
|
time.Sleep(time.Duration(RandRange(300, 950)) * time.Millisecond)
|
||||||
}
|
}
|
||||||
614
agent/wmi/wmi_windows.go
Normal file
614
agent/wmi/wmi_windows.go
Normal file
|
|
@ -0,0 +1,614 @@
|
||||||
|
package wmi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
rmm "github.com/amidaware/rmmagent/shared"
|
||||||
|
"github.com/yusufpapurcu/wmi"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetWMIInfo() map[string]interface{} {
|
||||||
|
wmiInfo := make(map[string]interface{})
|
||||||
|
|
||||||
|
compSysProd, err := GetWin32_ComputerSystemProduct()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
compSys, err := GetWin32_ComputerSystem()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
netAdaptConfig, err := GetWin32_NetworkAdapterConfiguration()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
physMem, err := GetWin32_PhysicalMemory()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
winOS, err := GetWin32_OperatingSystem()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
baseBoard, err := GetWin32_BaseBoard()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bios, err := GetWin32_BIOS()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
disk, err := GetWin32_DiskDrive()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
netAdapt, err := GetWin32_NetworkAdapter()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
desktopMon, err := GetWin32_DesktopMonitor()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu, err := GetWin32_Processor()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
usb, err := GetWin32_USBController()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics, err := GetWin32_VideoController()
|
||||||
|
if err != nil {
|
||||||
|
//a.Logger.Debugln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wmiInfo["comp_sys_prod"] = compSysProd
|
||||||
|
wmiInfo["comp_sys"] = compSys
|
||||||
|
wmiInfo["network_config"] = netAdaptConfig
|
||||||
|
wmiInfo["mem"] = physMem
|
||||||
|
wmiInfo["os"] = winOS
|
||||||
|
wmiInfo["base_board"] = baseBoard
|
||||||
|
wmiInfo["bios"] = bios
|
||||||
|
wmiInfo["disk"] = disk
|
||||||
|
wmiInfo["network_adapter"] = netAdapt
|
||||||
|
wmiInfo["desktop_monitor"] = desktopMon
|
||||||
|
wmiInfo["cpu"] = cpu
|
||||||
|
wmiInfo["usb"] = usb
|
||||||
|
wmiInfo["graphics"] = graphics
|
||||||
|
|
||||||
|
return wmiInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWin32_ComputerSystemProduct() ([]interface{}, error) {
|
||||||
|
var dst []rmm.Win32_ComputerSystemProduct
|
||||||
|
ret := make([]interface{}, 0)
|
||||||
|
q := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(q, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range dst {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
// this creates an extra unneeded array but keeping for now
|
||||||
|
// for backwards compatibility with the python agent
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWin32_ComputerSystem() ([]interface{}, error) {
|
||||||
|
var (
|
||||||
|
dstEX []rmm.Win32_ComputerSystemEX
|
||||||
|
dst []rmm.Win32_ComputerSystem
|
||||||
|
errEX error
|
||||||
|
errORIG error
|
||||||
|
fallback bool = false
|
||||||
|
)
|
||||||
|
ret := make([]interface{}, 0)
|
||||||
|
q := "SELECT * FROM Win32_ComputerSystem"
|
||||||
|
|
||||||
|
errEX = wmi.Query(q, &dstEX)
|
||||||
|
if errEX != nil {
|
||||||
|
errORIG = wmi.Query(q, &dst)
|
||||||
|
if errORIG != nil {
|
||||||
|
return ret, errORIG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errEX == nil {
|
||||||
|
for _, val := range dstEX {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
fallback = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// this creates an extra unneeded array but keeping for now
|
||||||
|
// for backwards compatibility with the python agent
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
if !fallback {
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errORIG == nil {
|
||||||
|
for _, val := range dst {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWin32_NetworkAdapterConfiguration() ([]interface{}, error) {
|
||||||
|
var dst []rmm.Win32_NetworkAdapterConfiguration
|
||||||
|
ret := make([]interface{}, 0)
|
||||||
|
|
||||||
|
q := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(q, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range dst {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// this creates an extra unneeded array but keeping for now
|
||||||
|
// for backwards compatibility with the python agent
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWin32_PhysicalMemory() ([]interface{}, error) {
|
||||||
|
var (
|
||||||
|
dstEX []rmm.Win32_PhysicalMemoryEX
|
||||||
|
dst []rmm.Win32_PhysicalMemory
|
||||||
|
errEX error
|
||||||
|
errORIG error
|
||||||
|
fallback bool = false
|
||||||
|
)
|
||||||
|
|
||||||
|
ret := make([]interface{}, 0)
|
||||||
|
q := "SELECT * FROM Win32_PhysicalMemory"
|
||||||
|
errEX = wmi.Query(q, &dstEX)
|
||||||
|
if errEX != nil {
|
||||||
|
errORIG = wmi.Query(q, &dst)
|
||||||
|
if errORIG != nil {
|
||||||
|
return ret, errORIG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errEX == nil {
|
||||||
|
for _, val := range dstEX {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
fallback = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// this creates an extra unneeded array but keeping for now
|
||||||
|
// for backwards compatibility with the python agent
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
if !fallback {
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errORIG == nil {
|
||||||
|
for _, val := range dst {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWin32_OperatingSystem() ([]interface{}, error) {
|
||||||
|
var dst []rmm.Win32_OperatingSystem
|
||||||
|
ret := make([]interface{}, 0)
|
||||||
|
q := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(q, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range dst {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// this creates an extra unneeded array but keeping for now
|
||||||
|
// for backwards compatibility with the python agent
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWin32_BaseBoard() ([]interface{}, error) {
|
||||||
|
var dst []rmm.Win32_BaseBoard
|
||||||
|
ret := make([]interface{}, 0)
|
||||||
|
q := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(q, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range dst {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// this creates an extra unneeded array but keeping for now
|
||||||
|
// for backwards compatibility with the python agent
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWin32_BIOS() ([]interface{}, error) {
|
||||||
|
var (
|
||||||
|
dstEX []rmm.Win32_BIOSEX
|
||||||
|
dst []rmm.Win32_BIOS
|
||||||
|
errEX error
|
||||||
|
errORIG error
|
||||||
|
fallback bool = false
|
||||||
|
)
|
||||||
|
ret := make([]interface{}, 0)
|
||||||
|
q := "SELECT * FROM Win32_BIOS"
|
||||||
|
|
||||||
|
errEX = wmi.Query(q, &dstEX)
|
||||||
|
if errEX != nil {
|
||||||
|
errORIG = wmi.Query(q, &dst)
|
||||||
|
if errORIG != nil {
|
||||||
|
return ret, errORIG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errEX == nil {
|
||||||
|
for _, val := range dstEX {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
fallback = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// this creates an extra unneeded array but keeping for now
|
||||||
|
// for backwards compatibility with the python agent
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fallback {
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errORIG == nil {
|
||||||
|
for _, val := range dst {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWin32_DiskDrive() ([]interface{}, error) {
|
||||||
|
var dst []rmm.Win32_DiskDrive
|
||||||
|
ret := make([]interface{}, 0)
|
||||||
|
q := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(q, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range dst {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// this creates an extra unneeded array but keeping for now
|
||||||
|
// for backwards compatibility with the python agent
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWin32_NetworkAdapter() ([]interface{}, error) {
|
||||||
|
var dst []rmm.Win32_NetworkAdapter
|
||||||
|
ret := make([]interface{}, 0)
|
||||||
|
q := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(q, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range dst {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// this creates an extra unneeded array but keeping for now
|
||||||
|
// for backwards compatibility with the python agent
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWin32_DesktopMonitor() ([]interface{}, error) {
|
||||||
|
var dst []rmm.Win32_DesktopMonitor
|
||||||
|
ret := make([]interface{}, 0)
|
||||||
|
|
||||||
|
q := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(q, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range dst {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// this creates an extra unneeded array but keeping for now
|
||||||
|
// for backwards compatibility with the python agent
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWin32_Processor() ([]interface{}, error) {
|
||||||
|
var (
|
||||||
|
dstEX []rmm.Win32_ProcessorEX
|
||||||
|
dst []rmm.Win32_Processor
|
||||||
|
errEX error
|
||||||
|
errORIG error
|
||||||
|
fallback bool = false
|
||||||
|
)
|
||||||
|
ret := make([]interface{}, 0)
|
||||||
|
q := "SELECT * FROM Win32_Processor"
|
||||||
|
|
||||||
|
errEX = wmi.Query(q, &dstEX)
|
||||||
|
if errEX != nil {
|
||||||
|
errORIG = wmi.Query(q, &dst)
|
||||||
|
if errORIG != nil {
|
||||||
|
return ret, errORIG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errEX == nil {
|
||||||
|
for _, val := range dstEX {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
fallback = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// this creates an extra unneeded array but keeping for now
|
||||||
|
// for backwards compatibility with the python agent
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fallback {
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errORIG == nil {
|
||||||
|
for _, val := range dst {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWin32_USBController() ([]interface{}, error) {
|
||||||
|
var dst []rmm.Win32_USBController
|
||||||
|
ret := make([]interface{}, 0)
|
||||||
|
|
||||||
|
q := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(q, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range dst {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// this creates an extra unneeded array but keeping for now
|
||||||
|
// for backwards compatibility with the python agent
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWin32_VideoController() ([]interface{}, error) {
|
||||||
|
var dst []rmm.Win32_VideoController
|
||||||
|
ret := make([]interface{}, 0)
|
||||||
|
q := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(q, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range dst {
|
||||||
|
b, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// this creates an extra unneeded array but keeping for now
|
||||||
|
// for backwards compatibility with the python agent
|
||||||
|
tmp := make([]interface{}, 0)
|
||||||
|
var un map[string]interface{}
|
||||||
|
if err := json.Unmarshal(b, &un); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = append(tmp, un)
|
||||||
|
ret = append(ret, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue