From 20dab1154d04a0e06e7386d3c1678e414c87fda0 Mon Sep 17 00:00:00 2001 From: wanyaoqi Date: Mon, 28 Jan 2019 20:07:04 +0800 Subject: [PATCH] win driver --- pkg/baremetal/manager.go | 2 +- pkg/baremetal/options/options.go | 2 + pkg/hostman/guestfs/core.go | 30 +- pkg/hostman/guestfs/fsdriver/drivers.go | 2 +- pkg/hostman/guestfs/fsdriver/interface.go | 3 +- pkg/hostman/guestfs/fsdriver/linux.go | 2 +- pkg/hostman/guestfs/fsdriver/windows.go | 422 ++++++++++++- pkg/hostman/guestfs/kvmpart.go | 4 +- pkg/hostman/guestfs/localfs.go | 30 +- pkg/hostman/guestfs/sshpart/sshpart.go | 26 +- pkg/hostman/guestman/guestman.go | 4 +- pkg/hostman/hostinfo/hostinfo.go | 6 +- pkg/hostman/hostmetrics/hostmetrics.go | 2 +- pkg/hostman/options/options.go | 3 +- pkg/util/winutils/winutils.go | 683 ++++++++++++++++++++++ 15 files changed, 1166 insertions(+), 55 deletions(-) create mode 100644 pkg/util/winutils/winutils.go diff --git a/pkg/baremetal/manager.go b/pkg/baremetal/manager.go index 88149c7f75..101e8cae85 100644 --- a/pkg/baremetal/manager.go +++ b/pkg/baremetal/manager.go @@ -1418,7 +1418,7 @@ func (s *SBaremetalServer) DoDeploy(term *ssh.Client, data jsonutils.JSONObject, if resetPassword && len(password) == 0 { password = seclib.RandomPassword(12) } - deployInfo := guestfs.NewDeployInfo(publicKey, deploys, password, isInit, true, o.Options.LinuxDefaultRootUser) + deployInfo := guestfs.NewDeployInfo(publicKey, deploys, password, isInit, true, o.Options.LinuxDefaultRootUser, o.Options.WindowsDefaultAdminUser) return s.deployFs(term, deployInfo) } diff --git a/pkg/baremetal/options/options.go b/pkg/baremetal/options/options.go index b357b4a569..5fa018a65c 100644 --- a/pkg/baremetal/options/options.go +++ b/pkg/baremetal/options/options.go @@ -26,6 +26,8 @@ type BaremetalOptions struct { DefaultIpmiPassword string `help:"Default IPMI passowrd"` DefaultStrongIpmiPassword string `help:"Default strong IPMI passowrd"` + + WindowsDefaultAdminUser bool `default:"true" help:"Default account for Windows system is Administrator"` } var ( diff --git a/pkg/hostman/guestfs/core.go b/pkg/hostman/guestfs/core.go index 559d822a97..b576dfbce6 100644 --- a/pkg/hostman/guestfs/core.go +++ b/pkg/hostman/guestfs/core.go @@ -15,12 +15,13 @@ import ( ) type SDeployInfo struct { - publicKey *sshkeys.SSHKeys - deploys []jsonutils.JSONObject - password string - isInit bool - enableTty bool - defaultRootUser bool + publicKey *sshkeys.SSHKeys + deploys []jsonutils.JSONObject + password string + isInit bool + enableTty bool + defaultRootUser bool + windowsDefaultAdminUser bool } func NewDeployInfo( @@ -30,14 +31,16 @@ func NewDeployInfo( isInit bool, enableTty bool, defaultRootUser bool, + windowsDefaultAdminUser bool, ) *SDeployInfo { return &SDeployInfo{ - publicKey: publicKey, - deploys: deploys, - password: password, - isInit: isInit, - enableTty: enableTty, - defaultRootUser: defaultRootUser, + publicKey: publicKey, + deploys: deploys, + password: password, + isInit: isInit, + enableTty: enableTty, + defaultRootUser: defaultRootUser, + windowsDefaultAdminUser: windowsDefaultAdminUser, } } @@ -176,7 +179,8 @@ func DeployGuestFs( } } if len(deployInfo.password) > 0 { - if account := rootfs.GetLoginAccount(partition, deployInfo.defaultRootUser); len(account) > 0 { + if account := rootfs.GetLoginAccount(partition, + deployInfo.defaultRootUser, deployInfo.windowsDefaultAdminUser); len(account) > 0 { ret.Set("account", jsonutils.NewString(account)) if err = rootfs.DeployPublicKey(partition, account, deployInfo.publicKey); err != nil { return nil, fmt.Errorf("DeployPublicKey: %v", err) diff --git a/pkg/hostman/guestfs/fsdriver/drivers.go b/pkg/hostman/guestfs/fsdriver/drivers.go index 2b052364a2..7ee33c1cc8 100644 --- a/pkg/hostman/guestfs/fsdriver/drivers.go +++ b/pkg/hostman/guestfs/fsdriver/drivers.go @@ -17,6 +17,6 @@ func init() { rootfsDrivers = append(rootfsDrivers, linuxFsDrivers...) //rootfsDrivers = append(rootfsDrivers, NewMacOSRootFs) //rootfsDrivers = append(rootfsDrivers, NewEsxiRootFs) - //rootfsDrivers = append(rootfsDrivers, NewWindowsRootFs) + rootfsDrivers = append(rootfsDrivers, NewWindowsRootFs) //rootfsDrivers = append(rootfsDrivers, NewAndroidRootFs) } diff --git a/pkg/hostman/guestfs/fsdriver/interface.go b/pkg/hostman/guestfs/fsdriver/interface.go index ec334ced29..a7006db48a 100644 --- a/pkg/hostman/guestfs/fsdriver/interface.go +++ b/pkg/hostman/guestfs/fsdriver/interface.go @@ -9,6 +9,7 @@ import ( ) type IDiskPartition interface { + GetLocalPath(sPath string, caseInsensitive bool) string FileGetContents(sPath string, caseInsensitive bool) ([]byte, error) FilePutContents(sPath, content string, modAppend, caseInsensitive bool) error Exists(sPath string, caseInsensitive bool) bool @@ -44,7 +45,7 @@ type IRootFsDriver interface { DeployStandbyNetworkingScripts(part IDiskPartition, nics, nicsStandby []jsonutils.JSONObject) error DeployUdevSubsystemScripts(IDiskPartition) error DeployFstabScripts(IDiskPartition, []jsonutils.JSONObject) error - GetLoginAccount(IDiskPartition, bool) string + GetLoginAccount(IDiskPartition, bool, bool) string DeployPublicKey(IDiskPartition, string, *sshkeys.SSHKeys) error ChangeUserPasswd(part IDiskPartition, account, gid, publicKey, password string) (string, error) DeployYunionroot(IDiskPartition, *sshkeys.SSHKeys) error diff --git a/pkg/hostman/guestfs/fsdriver/linux.go b/pkg/hostman/guestfs/fsdriver/linux.go index 3b94b96098..4f11ee9b85 100644 --- a/pkg/hostman/guestfs/fsdriver/linux.go +++ b/pkg/hostman/guestfs/fsdriver/linux.go @@ -60,7 +60,7 @@ func (l *sLinuxRootFs) DeployHosts(rootFs IDiskPartition, hostname, domain strin return rootFs.FilePutContents(etcHosts, hf.String(), false, false) } -func (l *sLinuxRootFs) GetLoginAccount(rootFs IDiskPartition, defaultRootUser bool) string { +func (l *sLinuxRootFs) GetLoginAccount(rootFs IDiskPartition, defaultRootUser bool, windowsDefaultAdminUser bool) string { var selUsr string if defaultRootUser && rootFs.Exists("/root", false) { selUsr = ROOT_USER diff --git a/pkg/hostman/guestfs/fsdriver/windows.go b/pkg/hostman/guestfs/fsdriver/windows.go index f869db648b..4554fbcf3b 100644 --- a/pkg/hostman/guestfs/fsdriver/windows.go +++ b/pkg/hostman/guestfs/fsdriver/windows.go @@ -1,9 +1,425 @@ package fsdriver +import ( + "crypto/md5" + "fmt" + "math/rand" + "path" + "regexp" + "strings" + "syscall" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/cloudcommon/sshkeys" + "yunion.io/x/onecloud/pkg/cloudcommon/types" + "yunion.io/x/onecloud/pkg/hostman/options" + "yunion.io/x/onecloud/pkg/util/fileutils2" + "yunion.io/x/onecloud/pkg/util/netutils2" + "yunion.io/x/onecloud/pkg/util/seclib2" + "yunion.io/x/onecloud/pkg/util/version" + "yunion.io/x/onecloud/pkg/util/winutils" + "yunion.io/x/pkg/utils" +) + +const ( + TCPIP_PARAM_KEY = `HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters` + BOOT_SCRIPT_PATH = "/Windows/System32/GroupPolicy/Machine/Scripts/Startup/cloudboot.bat" + WIN_BOOT_SCRIPT_PATH = "cloudboot.bat" +) + type SWindowsRootFs struct { *sGuestRootFsDriver + + guestDebugLogPath string + bootScripts string } -//func NewWindowsRootFs(part IDiskPartition) IRootFsDriver { -//return &SWindowsRootFs{sGuestRootFsDriver: newGuestRootFsDriver(part)} -//} +func NewWindowsRootFs(part IDiskPartition) IRootFsDriver { + seq := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'} + suffix := make([]byte, 16) + lenSeq := len(seq) + for i := 0; i < 16; i++ { + suffix[i] = seq[rand.Intn(lenSeq)] + } + return &SWindowsRootFs{ + sGuestRootFsDriver: newGuestRootFsDriver(part), + guestDebugLogPath: `%SystemRoot%\mdbg_` + string(suffix), + } +} + +func (w *SWindowsRootFs) IsFsCaseInsensitive() bool { + return true +} + +func (w *SWindowsRootFs) GetName() string { + return "Windows" +} + +func (w *SWindowsRootFs) DeployPublicKey(IDiskPartition, string, *sshkeys.SSHKeys) error { + return nil +} + +func (w *SWindowsRootFs) RootSignatures() []string { + return []string{ + "/program files", "/windows", + "/windows/system32/drivers/etc", "/windows/system32/config", + "/windows/system32/config/sam", + "/windows/system32/config/software", + "/windows/system32/config/system", + } +} + +func (w *SWindowsRootFs) GetReleaseInfo(IDiskPartition) *SReleaseInfo { + confPath := w.rootFs.GetLocalPath("/windows/system32/config", true) + tool := winutils.NewWinRegTool(confPath) + if tool.CheckPath() { + distro := tool.GetProductName() + version := tool.GetVersion() + arch := tool.GetArch() + lan := tool.GetInstallLanguage() + return &SReleaseInfo{distro, version, arch, lan} + } else { + return nil + } +} + +func (w *SWindowsRootFs) GetLoginAccount(rootFs IDiskPartition, defaultRootUser bool, windowsDefaultAdminUser bool) string { + confPath := w.rootFs.GetLocalPath("/windows/system32/config", true) + tool := winutils.NewWinRegTool(confPath) + users := tool.GetUsers() + admin := "Administrator" + selUsr := "" + if w.IsWindows10() { + delete(users, admin) + } + if _, ok := users[admin]; ok && windowsDefaultAdminUser { + selUsr = admin + } else { + for user, _ := range users { + if user != admin && (len(selUsr) == 0 || len(selUsr) > len(user)) { + selUsr = user + } + } + if _, ok := users[admin]; ok && len(selUsr) == 0 { + selUsr = admin + } + } + if len(selUsr) > 0 { + if _, ok := users[selUsr]; !ok { + tool.UnlockUser(selUsr) + } + } + return selUsr +} + +func (w *SWindowsRootFs) IsWindows10() bool { + info := w.GetReleaseInfo(nil) + if info != nil && strings.HasPrefix(info.Distro, "Windows 10 ") { + return true + } + return false + +} + +func (w *SWindowsRootFs) GetOs() string { + return "Windows" +} + +func (w *SWindowsRootFs) appendGuestBootScript(content string) string { + w.bootScripts += "\r\n" + content + return w.bootScripts +} + +func (w *SWindowsRootFs) regAdd(path, key, val, regType string) string { + return fmt.Sprintf(`REG ADD %s /V "%s" /D "%s" /T %s /F`, path, key, val, regType) +} + +func (w *SWindowsRootFs) putGuestScriptContents(spath, content string) error { + contentArr := []string{} + contentLen := len(content) + + var j = 0 + for i := 1; i < contentLen; i++ { + if content[i] == '\n' && content[i-1] != '\r' { + contentArr = append(contentArr, content[j:i]) + j = i + 1 + } + } + if j < contentLen { + contentArr = append(contentArr, content[j:]) + } + + content = strings.Join(contentArr, "\r\n") + + return w.rootFs.FilePutContents(spath, content, false, true) +} + +func (w *SWindowsRootFs) DeployHostname(part IDiskPartition, hostname, domain string) error { + bootScript := strings.Join([]string{ + `set HOSTNAME_SCRIPT=%SystemRoot%\hostnamecfg.bat`, + `if exist %HOSTNAME_SCRIPT% (`, + ` call %HOSTNAME_SCRIPT%`, + ` del %HOSTNAME_SCRIPT%`, + `)`, + }, "\r\n") + w.appendGuestBootScript(bootScript) + + lines := []string{} + for k, v := range map[string]string{ + "Hostname": hostname, + "Domain": domain, + "NV Hostname": hostname, + "NV Domain": domain, + } { + lines = append(lines, w.regAdd(TCPIP_PARAM_KEY, k, v, "REG_SZ")) + } + hostScripts := strings.Join(lines, "\r\n") + return w.putGuestScriptContents("/windows/hostnamecfg.bat", hostScripts) +} + +func (w *SWindowsRootFs) DeployHosts(part IDiskPartition, hn, domain string, ips []string) error { + var ( + ETC_HOSTS = "/windows/system32/drivers/etc/hosts" + oldHf = "" + ) + + if w.rootFs.Exists(ETC_HOSTS, true) { + oldHfBytes, err := w.rootFs.FileGetContents(ETC_HOSTS, true) + if err != nil { + log.Errorln(err) + return err + } + oldHf = string(oldHfBytes) + } + + hf := fileutils2.HostsFile{} + hf.Parse(oldHf) + hf.Add("127.0.0.1", "localhost") + for _, ip := range ips { + hf.Add(ip, fmt.Sprintf("%s.%s", hn, domain), hn) + } + return w.rootFs.FilePutContents(ETC_HOSTS, hf.String(), false, true) +} + +func (w *SWindowsRootFs) DeployNetworkingScripts(rootfs IDiskPartition, nics []jsonutils.JSONObject) error { + mainNic, err := netutils2.GetMainNic(nics) + if err != nil { + return err + } + mainIp := "" + if mainNic != nil { + mainIp, _ = mainNic.GetString("ip") + } + bootScript := strings.Join([]string{ + `set NETCFG_SCRIPT=%SystemRoot%\netcfg.bat`, + `if exist %NETCFG_SCRIPT% (`, + ` call %NETCFG_SCRIPT%`, + ` del %NETCFG_SCRIPT%`, + `)`, + }, "\r\n") + w.appendGuestBootScript(bootScript) + lines := []string{ + "@echo off", + w.MakeGuestDebugCmd("netcfg step 1"), + "setlocal enableDelayedExpansion", + `for /f "delims=" %%a in (\'getmac /fo csv /nh /v\') do (`, + ` set line=%%a&set line=!line:"=,!`, + ` for /f "delims=,,, tokens=1,3" %%b in ("!line!") do (`, + } + + for _, nic := range nics { + snic := &types.SServerNic{} + if err := nic.Unmarshal(snic); err != nil { + log.Errorln(err) + return err + } + + mac := snic.Mac + mac = strings.Replace(strings.ToUpper(mac), ":", "-", -1) + lines = append(lines, fmt.Sprintf(` if "%%%%c" == "%s" (`, mac)) + if jsonutils.QueryBoolean(nic, "manual", false) { + netmask := netutils2.Netlen2Mask(snic.Masklen) + cfg := fmt.Sprintf(` netsh interface ip set address "%%%%b" static %s %s`, snic.Ip, netmask) + if len(snic.Gateway) > 0 && snic.Ip == mainIp { + cfg += fmt.Sprintf(" %s", snic.Gateway) + } + lines = append(lines, cfg) + routes := [][]string{} + netutils2.AddNicRoutes(&routes, snic, mainIp, len(nics), options.HostOptions.PrivatePrefixes) + for _, r := range routes { + lines = append(lines, fmt.Sprintf(` netsh interface ip add route %s "%%%%b" %s`, r[0], r[1])) + } + dnslist := netutils2.GetNicDns(snic) + if len(dnslist) > 0 { + lines = append(lines, fmt.Sprintf( + ` netsh interface ip set dns name="%%%%b" source=static addr=%s ddns=disabled suffix=interface`, dnslist[0])) + if len(dnslist) > 1 { + for i := 1; i < len(dnslist); i++ { + lines = append(lines, fmt.Sprintf(` netsh interface ip add dns "%%%%b" %s index=%d`, dnslist[i], i+1)) + } + } + } + + if len(snic.Domain) > 0 && snic.Ip == mainIp { + lines = append(lines, w.regAdd(TCPIP_PARAM_KEY, "SearchList", snic.Domain, "REG_SZ")) + } + } else { + lines = append(lines, ` netsh interface ip set address "%%b" dhcp`) + lines = append(lines, ` netsh interface ip set dns "%%b" dhcp`) + } + lines = append(lines, ` )`) + } + lines = append(lines, ` )`) + lines = append(lines, `)`) + lines = append(lines, w.MakeGuestDebugCmd("netcfg step 2")) + lines = append(lines, `netsh advfirewall firewall set rule group=\"remote desktop\" new enable=yes`) + netScript := strings.Join(lines, "\r\n") + return w.putGuestScriptContents("/windows/netcfg.bat", netScript) +} + +func (w *SWindowsRootFs) MakeGuestDebugCmd(content string) string { + mark := "=============" + content = regexp.MustCompile(`(["^&<>|])`).ReplaceAllString(content, "^$1") + return fmt.Sprintf("echo %s %s %s >> %s", mark, content, mark, w.guestDebugLogPath) +} + +func (w *SWindowsRootFs) prependGuestBootScript(content string) { + w.bootScripts = content + "\r\n" + w.bootScripts +} + +func (w *SWindowsRootFs) PrepareFsForTemplate(IDiskPartition) error { + for _, f := range []string{"/Pagefile.sys", "/Hiberfil.sys", "/Swapfile.sys"} { + if w.rootFs.Exists(f, true) { + w.rootFs.Remove(f, true) + } + } + return nil +} + +func (w *SWindowsRootFs) CommitChanges(part IDiskPartition) error { + confPath := part.GetLocalPath("/windows/system32/config", true) + tool := winutils.NewWinRegTool(confPath) + tool.CheckPath() + tool.EnableRdp() + tool.InstallGpeditStartScript(WIN_BOOT_SCRIPT_PATH) + if err := w.rootFs.Mkdir(path.Dir(BOOT_SCRIPT_PATH), syscall.S_IRUSR|syscall.S_IWUSR|syscall.S_IXUSR, true); err != nil { + return err + } + return w.rootFs.FilePutContents(BOOT_SCRIPT_PATH, w.bootScripts, false, false) +} + +func (w *SWindowsRootFs) ChangeUserPasswd(part IDiskPartition, account, gid, publicKey, password string) (string, error) { + rinfo := w.GetReleaseInfo(part) + confPath := part.GetLocalPath("/windows/system32/config", true) + tool := winutils.NewWinRegTool(confPath) + tool.CheckPath() + success := false + if rinfo != nil && version.GE(rinfo.Version, "6.1") { + success = w.deployPublicKeyByGuest(account, password) + } else { + success = tool.ChangePassword(account, password) == nil + } + + var ( + secret string + err error + ) + if success { + if len(publicKey) > 0 { + secret, err = seclib2.EncryptBase64(publicKey, password) + if err != nil { + return "", err + } + } else { + secret, err = utils.EncryptAESBase64(gid, password) + if err != nil { + return "", err + } + } + if rinfo != nil && strings.Contains(rinfo.Distro, "Windows XP") { + if len(tool.GetLogontype()) > 0 { + tool.SetLogontype("0x0") + } + } + } else { + log.Errorf("Filaed Password %s", account) + } + defUanme := tool.GetDefaultAccount() + if len(defUanme) > 0 && defUanme != account { + tool.SetDefaultAccount(account) + } + return secret, nil +} + +func (w *SWindowsRootFs) deployPublicKeyByGuest(uname, passwd string) bool { + if !w.deploySetupCompleteScripts(uname, passwd) { + return false + } + bootScript := strings.Join([]string{ + `set CHANGE_PASSWD_SCRIPT=%SystemRoot%\chgpwd.bat`, + `if exist %CHANGE_PASSWD_SCRIPT% (`, + ` call %CHANGE_PASSWD_SCRIPT%`, + ` del %CHANGE_PASSWD_SCRIPT%`, + `)`, + }, "\r\n") + w.prependGuestBootScript(bootScript) + logPath := w.guestDebugLogPath + chksum := md5.Sum([]byte(passwd + logPath[(len(logPath)-10):])) + + chgpwdScript := strings.Join([]string{ + w.MakeGuestDebugCmd("change password step 1"), + strings.Join([]string{ + `%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe`, + ` -executionpolicy bypass %SystemRoot%\chgpwd.ps1`, + fmt.Sprintf(" %s %s %s %s", uname, passwd, chksum, logPath), + }, ""), + `del %SystemRoot%\chgpwd.ps1`, + w.MakeGuestDebugCmd("change password step 2"), + }, "\r\n") + if w.putGuestScriptContents("/windows/chgpwd.bat", chgpwdScript) != nil { + return false + } + if w.putGuestScriptContents("/windows/chgpwd.ps1", WinScriptChangePassword) != nil { + return false + } + return true +} + +func (w *SWindowsRootFs) deploySetupCompleteScripts(uname, passwd string) bool { + SETUP_SCRIPT_PATH := "/Windows/Setup/Scripts/SetupComplete.cmd" + if !w.rootFs.Exists(path.Dir(SETUP_SCRIPT_PATH), true) { + w.rootFs.Mkdir(path.Dir(SETUP_SCRIPT_PATH), + syscall.S_IRUSR|syscall.S_IWUSR|syscall.S_IXUSR, true) + } + if w.putGuestScriptContents("/windows/chgpwd_setup.ps1", WinScriptChangePassword) != nil { + return false + } + cmds := []string{ + `%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass %SystemRoot%\chgpwd_setup.ps1 '` + + fmt.Sprintf("%s %s", uname, passwd), + "Net stop wuauserv", + } + for _, v := range [][3]string{ + [3]string{"AUOptions", "REG_DWORD", "3"}, + [3]string{"NoAutoUpdate", "REG_DWORD", "0"}, + [3]string{"ScheduledInstallDay", "REG_DWORD", "0"}, + [3]string{"ScheduledInstallTime", "REG_DWORD", "4"}, + [3]string{"AutoInstallMinorUpdates", "REG_DWORD", "1"}, + [3]string{"NoAutoRebootWithLoggedOnUsers", "REG_DWORD", "1"}, + [3]string{"IncludeRecommendedUpdates", "REG_DWORD", "0"}, + [3]string{"EnableFeaturedSoftware", "REG_DWORD", "1"}, + } { + cmds = append(cmds, `REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v %s /t %s /d %s /f`, + v[0], v[1], v[2]) + } + cmds = append(cmds, "Net start wuauserv") + cmds = append(cmds, "wuauclt /detectnow") + cmds = append(cmds, `del %SystemRoot%\chgpwd_setup.ps1`) + cmds = append(cmds, `del %SystemRoot%\Setup\Scripts\SetupComplete.cmd`) + if w.putGuestScriptContents(SETUP_SCRIPT_PATH, strings.Join(cmds, "\r\n")) != nil { + return false + } + return true +} diff --git a/pkg/hostman/guestfs/kvmpart.go b/pkg/hostman/guestfs/kvmpart.go index accce5ec91..afa27ca992 100644 --- a/pkg/hostman/guestfs/kvmpart.go +++ b/pkg/hostman/guestfs/kvmpart.go @@ -116,7 +116,7 @@ func (p *SKVMGuestDiskPartition) fsck() error { if err == nil { break } else { - return err + continue } } } @@ -125,7 +125,7 @@ func (p *SKVMGuestDiskPartition) fsck() error { } func (p *SKVMGuestDiskPartition) Exists(sPath string, caseInsensitive bool) bool { - sPath = p.getLocalPath(sPath, caseInsensitive) + sPath = p.GetLocalPath(sPath, caseInsensitive) if len(sPath) > 0 { return fileutils2.Exists(sPath) } diff --git a/pkg/hostman/guestfs/localfs.go b/pkg/hostman/guestfs/localfs.go index 7409fed896..94afa5e31c 100644 --- a/pkg/hostman/guestfs/localfs.go +++ b/pkg/hostman/guestfs/localfs.go @@ -23,7 +23,7 @@ func (f *SLocalGuestFS) SupportSerialPorts() bool { return false } -func (f *SLocalGuestFS) getLocalPath(sPath string, caseInsensitive bool) string { +func (f *SLocalGuestFS) GetLocalPath(sPath string, caseInsensitive bool) string { var fullPath = f.mountPath pathSegs := strings.Split(sPath, "/") for _, seg := range pathSegs { @@ -51,7 +51,7 @@ func (f *SLocalGuestFS) getLocalPath(sPath string, caseInsensitive bool) string } func (f *SLocalGuestFS) Remove(path string, caseInsensitive bool) { - path = f.getLocalPath(path, caseInsensitive) + path = f.GetLocalPath(path, caseInsensitive) if len(path) > 0 { os.Remove(path) } @@ -60,16 +60,16 @@ func (f *SLocalGuestFS) Remove(path string, caseInsensitive bool) { func (f *SLocalGuestFS) Mkdir(sPath string, mode int, caseInsensitive bool) error { segs := strings.Split(sPath, "/") sPath = "" - pPath := f.getLocalPath("/", caseInsensitive) + pPath := f.GetLocalPath("/", caseInsensitive) for _, s := range segs { if len(s) > 0 { sPath = path.Join(sPath, s) - vPath := f.getLocalPath(sPath, caseInsensitive) + vPath := f.GetLocalPath(sPath, caseInsensitive) if len(vPath) == 0 { if err := os.Mkdir(path.Join(pPath, s), os.FileMode(mode)); err != nil { return err } - pPath = f.getLocalPath(sPath, caseInsensitive) + pPath = f.GetLocalPath(sPath, caseInsensitive) } else { pPath = vPath } @@ -79,7 +79,7 @@ func (f *SLocalGuestFS) Mkdir(sPath string, mode int, caseInsensitive bool) erro } func (f *SLocalGuestFS) ListDir(sPath string, caseInsensitive bool) []string { - sPath = f.getLocalPath(sPath, caseInsensitive) + sPath = f.GetLocalPath(sPath, caseInsensitive) if len(sPath) > 0 { files, err := ioutil.ReadDir(sPath) if err != nil { @@ -96,7 +96,7 @@ func (f *SLocalGuestFS) ListDir(sPath string, caseInsensitive bool) []string { } func (f *SLocalGuestFS) Cleandir(dir string, keepdir, caseInsensitive bool) error { - sPath := f.getLocalPath(dir, caseInsensitive) + sPath := f.GetLocalPath(dir, caseInsensitive) if len(sPath) > 0 { return fileutils2.Cleandir(sPath, keepdir) } @@ -104,7 +104,7 @@ func (f *SLocalGuestFS) Cleandir(dir string, keepdir, caseInsensitive bool) erro } func (f *SLocalGuestFS) Zerofiles(dir string, caseInsensitive bool) error { - sPath := f.getLocalPath(dir, caseInsensitive) + sPath := f.GetLocalPath(dir, caseInsensitive) if len(sPath) > 0 { return fileutils2.Zerofiles(sPath) } @@ -149,7 +149,7 @@ func (f *SLocalGuestFS) Passwd(account, password string, caseInsensitive bool) e } func (f *SLocalGuestFS) Stat(usrDir string, caseInsensitive bool) os.FileInfo { - sPath := f.getLocalPath(usrDir, caseInsensitive) + sPath := f.GetLocalPath(usrDir, caseInsensitive) if len(sPath) > 0 { fileInfo, err := os.Stat(sPath) if err != nil { @@ -161,7 +161,7 @@ func (f *SLocalGuestFS) Stat(usrDir string, caseInsensitive bool) os.FileInfo { } func (f *SLocalGuestFS) Exists(sPath string, caseInsensitive bool) bool { - sPath = f.getLocalPath(sPath, caseInsensitive) + sPath = f.GetLocalPath(sPath, caseInsensitive) if len(sPath) > 0 { return fileutils2.Exists(sPath) } @@ -169,7 +169,7 @@ func (f *SLocalGuestFS) Exists(sPath string, caseInsensitive bool) bool { } func (f *SLocalGuestFS) Chown(sPath string, uid, gid int, caseInsensitive bool) error { - sPath = f.getLocalPath(sPath, caseInsensitive) + sPath = f.GetLocalPath(sPath, caseInsensitive) if len(sPath) > 0 { return os.Chown(sPath, uid, gid) } @@ -177,7 +177,7 @@ func (f *SLocalGuestFS) Chown(sPath string, uid, gid int, caseInsensitive bool) } func (f *SLocalGuestFS) Chmod(sPath string, mode uint32, caseInsensitive bool) error { - sPath = f.getLocalPath(sPath, caseInsensitive) + sPath = f.GetLocalPath(sPath, caseInsensitive) if len(sPath) > 0 { return os.Chmod(sPath, os.FileMode(mode)) } @@ -196,7 +196,7 @@ func (f *SLocalGuestFS) UserAdd(user string, caseInsensitive bool) error { } func (f *SLocalGuestFS) FileGetContents(sPath string, caseInsensitive bool) ([]byte, error) { - sPath = f.getLocalPath(sPath, caseInsensitive) + sPath = f.GetLocalPath(sPath, caseInsensitive) if len(sPath) > 0 { return ioutil.ReadFile(sPath) } @@ -204,11 +204,11 @@ func (f *SLocalGuestFS) FileGetContents(sPath string, caseInsensitive bool) ([]b } func (f *SLocalGuestFS) FilePutContents(sPath, content string, modAppend, caseInsensitive bool) error { - sFilePath := f.getLocalPath(sPath, caseInsensitive) + sFilePath := f.GetLocalPath(sPath, caseInsensitive) if len(sFilePath) > 0 { sPath = sFilePath } else { - dirPath := f.getLocalPath(path.Dir(sPath), caseInsensitive) + dirPath := f.GetLocalPath(path.Dir(sPath), caseInsensitive) if len(dirPath) > 0 { sPath = path.Join(dirPath, path.Base(sPath)) } diff --git a/pkg/hostman/guestfs/sshpart/sshpart.go b/pkg/hostman/guestfs/sshpart/sshpart.go index d1f3cdcc20..d31469d848 100644 --- a/pkg/hostman/guestfs/sshpart/sshpart.go +++ b/pkg/hostman/guestfs/sshpart/sshpart.go @@ -69,15 +69,15 @@ func (p *SSHPartition) osMkdirP(dir string, mode uint32) error { func (p *SSHPartition) Mkdir(sPath string, mode int, caseInsensitive bool) error { segs := strings.Split(sPath, "/") sp := "" - pPath := p.getLocalPath("/", caseInsensitive) + pPath := p.GetLocalPath("/", caseInsensitive) var err error for _, s := range segs { if len(s) > 0 { sp = path.Join(sp, s) - vPath := p.getLocalPath(sp, caseInsensitive) + vPath := p.GetLocalPath(sp, caseInsensitive) if len(vPath) == 0 { err = p.osMkdirP(path.Join(pPath, s), uint32(mode)) - pPath = p.getLocalPath(sp, caseInsensitive) + pPath = p.GetLocalPath(sp, caseInsensitive) } else { pPath = vPath } @@ -160,7 +160,7 @@ func (p *SSHPartition) IsMounted() bool { } func (p *SSHPartition) Chmod(sPath string, mode uint32, caseI bool) error { - sPath = p.getLocalPath(sPath, caseI) + sPath = p.GetLocalPath(sPath, caseI) if sPath != "" { return p.osChmod(sPath, mode) } @@ -193,7 +193,7 @@ func (p *SSHPartition) osListDir(path string) ([]string, error) { return files, nil } -func (p *SSHPartition) getLocalPath(sPath string, caseI bool) string { +func (p *SSHPartition) GetLocalPath(sPath string, caseI bool) string { var fullPath = p.mountPath pathSegs := strings.Split(sPath, "/") for _, seg := range pathSegs { @@ -221,7 +221,7 @@ func (p *SSHPartition) getLocalPath(sPath string, caseI bool) string { } func (p *SSHPartition) Exists(sPath string, caseInsensitive bool) bool { - sPath = p.getLocalPath(sPath, caseInsensitive) + sPath = p.GetLocalPath(sPath, caseInsensitive) if len(sPath) > 0 { return p.osPathExists(sPath) } @@ -242,7 +242,7 @@ func (p *SSHPartition) sshFileGetContents(path string) ([]byte, error) { } func (p *SSHPartition) FileGetContents(sPath string, caseInsensitive bool) ([]byte, error) { - sPath = p.getLocalPath(sPath, caseInsensitive) + sPath = p.GetLocalPath(sPath, caseInsensitive) if len(sPath) > 0 { return p.sshFileGetContents(sPath) } @@ -282,11 +282,11 @@ func (p *SSHPartition) sshFilePutContents(sPath, content string, modAppend bool) } func (p *SSHPartition) FilePutContents(sPath, content string, modAppend, caseInsensitive bool) error { - sFilePath := p.getLocalPath(sPath, caseInsensitive) + sFilePath := p.GetLocalPath(sPath, caseInsensitive) if len(sFilePath) > 0 { sPath = sFilePath } else { - dirPath := p.getLocalPath(path.Dir(sPath), caseInsensitive) + dirPath := p.GetLocalPath(path.Dir(sPath), caseInsensitive) if len(dirPath) > 0 { sPath = path.Join(dirPath, path.Base(sPath)) } @@ -298,7 +298,7 @@ func (p *SSHPartition) FilePutContents(sPath, content string, modAppend, caseIns } func (p *SSHPartition) ListDir(sPath string, caseInsensitive bool) []string { - sPath = p.getLocalPath(sPath, caseInsensitive) + sPath = p.GetLocalPath(sPath, caseInsensitive) if len(sPath) > 0 { ret, err := p.osListDir(sPath) if err != nil { @@ -317,7 +317,7 @@ func (p *SSHPartition) osChown(sPath string, uid, gid int) error { } func (p *SSHPartition) Chown(sPath string, uid, gid int, caseInsensitive bool) error { - sPath = p.getLocalPath(sPath, caseInsensitive) + sPath = p.GetLocalPath(sPath, caseInsensitive) if len(sPath) == 0 { return fmt.Errorf("Can't get local path: %s", sPath) } @@ -331,7 +331,7 @@ func (p *SSHPartition) osRemove(sPath string) error { } func (p *SSHPartition) Remove(sPath string, caseInsensitive bool) { - sPath = p.getLocalPath(sPath, caseInsensitive) + sPath = p.GetLocalPath(sPath, caseInsensitive) if len(sPath) > 0 { p.osRemove(sPath) } @@ -445,7 +445,7 @@ func (info sFileInfo) Sys() interface{} { } func (p *SSHPartition) Stat(sPath string, caseInsensitive bool) os.FileInfo { - sPath = p.getLocalPath(sPath, caseInsensitive) + sPath = p.GetLocalPath(sPath, caseInsensitive) if len(sPath) == 0 { return nil } diff --git a/pkg/hostman/guestman/guestman.go b/pkg/hostman/guestman/guestman.go index f96e4e5e7a..016426c3e8 100644 --- a/pkg/hostman/guestman/guestman.go +++ b/pkg/hostman/guestman/guestman.go @@ -291,7 +291,9 @@ func (m *SGuestManager) GuestDeploy(ctx context.Context, params interface{}) (js password = seclib.RandomPassword(12) } - guestInfo, err := guest.DeployFs(guestfs.NewDeployInfo(publicKey, deploys, password, deployParams.IsInit, false, options.HostOptions.LinuxDefaultRootUser)) + guestInfo, err := guest.DeployFs(guestfs.NewDeployInfo( + publicKey, deploys, password, deployParams.IsInit, false, + options.HostOptions.LinuxDefaultRootUser, options.HostOptions.WindowsDefaultAdminUser)) if err != nil { log.Errorf("Deploy guest fs error: %s", err) return nil, err diff --git a/pkg/hostman/hostinfo/hostinfo.go b/pkg/hostman/hostinfo/hostinfo.go index a94a6c34ec..16b7561817 100644 --- a/pkg/hostman/hostinfo/hostinfo.go +++ b/pkg/hostman/hostinfo/hostinfo.go @@ -34,6 +34,7 @@ import ( "yunion.io/x/onecloud/pkg/util/qemutils" "yunion.io/x/onecloud/pkg/util/sysutils" "yunion.io/x/onecloud/pkg/util/timeutils2" + "yunion.io/x/onecloud/pkg/util/winutils" ) var ( @@ -216,8 +217,9 @@ func (h *SHostInfo) prepareEnv() error { log.Errorf("Failed to activate nbd device: %s", output) } - // TODO: winRegTool还未实现 - // if not WinRegTool.check_tool(options.chntpw_path)... + if !winutils.CheckTool(options.HostOptions.ChntpwPath) { + return fmt.Errorf("Failed to find chntpw tool") + } if err := hostbridge.Prepare(options.HostOptions.BridgeDriver); err != nil { log.Errorln(err) diff --git a/pkg/hostman/hostmetrics/hostmetrics.go b/pkg/hostman/hostmetrics/hostmetrics.go index 12d88d4f54..5dc0d77a31 100644 --- a/pkg/hostman/hostmetrics/hostmetrics.go +++ b/pkg/hostman/hostmetrics/hostmetrics.go @@ -44,7 +44,7 @@ func Init() { func Start() { if hostMetricsCollector != nil { - hostMetricsCollector.Start() + go hostMetricsCollector.Start() } } diff --git a/pkg/hostman/options/options.go b/pkg/hostman/options/options.go index 537a0636ff..39fef19f08 100644 --- a/pkg/hostman/options/options.go +++ b/pkg/hostman/options/options.go @@ -86,7 +86,8 @@ type SHostOptions struct { SnapshotDirSuffix string `help:"Snapshot dir name equal diskId concat snapshot dir suffix" default:"_snap"` SnapshotRecycleDay int `default:"1" help:"Snapshot Recycle delete Duration day"` - EnableTelegraf bool `default:"true" help:"enable send monitoring data to telegraf"` + EnableTelegraf bool `default:"true" help:"enable send monitoring data to telegraf"` + WindowsDefaultAdminUser bool `default:"true" help:"Default account for Windows system is Administrator"` } var HostOptions SHostOptions diff --git a/pkg/util/winutils/winutils.go b/pkg/util/winutils/winutils.go new file mode 100644 index 0000000000..5f53856247 --- /dev/null +++ b/pkg/util/winutils/winutils.go @@ -0,0 +1,683 @@ +package winutils + +import ( + "crypto/md5" + "fmt" + "io" + "io/ioutil" + "os/exec" + "path" + "regexp" + "strconv" + "strings" + "syscall" + + "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/util/fileutils2" + "yunion.io/x/onecloud/pkg/util/procutils" + "yunion.io/x/onecloud/pkg/util/regutils2" + "yunion.io/x/pkg/utils" +) + +var _CHNTPW_PATH string + +func SetChntpwPath(spath string) { + _CHNTPW_PATH = spath +} + +func GetChntpwPath() string { + if len(_CHNTPW_PATH) == 0 { + _CHNTPW_PATH = "/usr/local/bin/chntpw.static" + } + return _CHNTPW_PATH +} + +const ( + SYSTEM = "system" + SOFTWARE = "software" + SECURITY = "security" + SAM = "sam" + CONFIRM = "y" +) + +func NewWinRegTool(spath string) *SWinRegTool { + return &SWinRegTool{ConfigPath: spath} +} + +func CheckTool(spath string) bool { + if fileutils2.Exists(spath) && exec.Command(spath, "-h").Run() == nil { + return true + } else { + return false + } +} + +type SWinRegTool struct { + ConfigPath string + SystemPath string + SoftwarePath string + SamPath string + SecurityPath string +} + +func (w *SWinRegTool) CheckPath() bool { + files, err := ioutil.ReadDir(w.ConfigPath) + if err != nil { + log.Errorln(err) + return false + } + + for _, file := range files { + switch strings.ToLower(file.Name()) { + case SYSTEM: + w.SystemPath = path.Join(w.ConfigPath, file.Name()) + case SOFTWARE: + w.SoftwarePath = path.Join(w.ConfigPath, file.Name()) + case SAM: + w.SamPath = path.Join(w.ConfigPath, file.Name()) + case SECURITY: + w.SecurityPath = path.Join(w.ConfigPath, file.Name()) + } + } + if len(w.SystemPath) > 0 && len(w.SoftwarePath) > 0 && + len(w.SamPath) > 0 && len(w.SecurityPath) > 0 { + return true + } + return false +} + +func (w *SWinRegTool) GetUsers() map[string]bool { + output, err := procutils.NewCommand(GetChntpwPath(), "-l", w.SamPath).Run() + if err != nil { + log.Errorln(err) + return nil + } + + users := map[string]bool{} + re := regexp.MustCompile( + `\|\s*\w+\s*\|\s*(?P\w+)\s*\|\s*(ADMIN)?\s*\|\s*(?P(dis/lock|\*BLANK\*)?)`) + for _, line := range strings.Split(string(output), "\n") { + m := regutils2.GetParams(re, line) + if len(m) > 0 { + user, _ := m["user"] + if strings.ToLower(user) != "guest" { + lock, _ := m["lock"] + users[user] = lock != "dis/lock" + } + } + } + return users +} + +func (w *SWinRegTool) samChange(user string, seq ...string) error { + proc := exec.Command(GetChntpwPath(), "-u", user, w.SamPath, w.SystemPath, w.SecurityPath) + stdin, err := proc.StdinPipe() + if err != nil { + return err + } + defer stdin.Close() + + outb, err := proc.StdoutPipe() + if err != nil { + return err + } + defer outb.Close() + + errb, err := proc.StderrPipe() + if err != nil { + return err + } + defer errb.Close() + + if err := proc.Start(); err != nil { + return err + } + + io.WriteString(stdin, "n\n") + for _, s := range seq { + io.WriteString(stdin, s+"\n") + } + io.WriteString(stdin, CONFIRM+"\n") + stdoutPut, err := ioutil.ReadAll(outb) + if err != nil { + return err + } + stderrOutPut, err := ioutil.ReadAll(errb) + if err != nil { + return err + } + log.Debugf("Sam change %s %s", stdoutPut, stderrOutPut) + if proc.ProcessState.Exited() { + if err := proc.Wait(); err != nil { + if exiterr, ok := err.(*exec.ExitError); ok { + ws := exiterr.Sys().(syscall.WaitStatus) + if ws.ExitStatus() == 2 { + return nil + } + } + log.Errorf("Failed to change SAM password") + return err + } else { + return nil + } + } else { + proc.Process.Kill() + return fmt.Errorf("Failed to change SAM password, not exit cleanly") + } +} + +func (w *SWinRegTool) ChangePassword(user, password string) error { + return w.samChange(user, "2", password) +} + +func (w *SWinRegTool) RemovePassword(user string) error { + return w.samChange(user, "2") +} + +func (w *SWinRegTool) UnlockUser(user string) error { + return w.samChange(user, "4") +} + +func (w *SWinRegTool) GetRegFile(regPath string) (string, []string) { + re := regexp.MustCompile("\\") + vals := re.Split(regPath, -1) + regSeg := []string{} + for _, val := range vals { + if len(val) > 0 { + regSeg = append(regSeg, val) + } + } + + if regSeg[0] == "HKLM" { + regSeg = regSeg[1:] + } + if strings.ToLower(regSeg[0]) == SOFTWARE { + regSeg = regSeg[1:] + return w.SoftwarePath, regSeg + } else if strings.ToLower(regSeg[0]) == SYSTEM { + regSeg = regSeg[1:] + return w.SystemPath, regSeg + } else { + return "", nil + } +} + +func (w *SWinRegTool) showRegistry(spath string, keySeg []string, verb string) ([]string, error) { + proc := exec.Command(GetChntpwPath(), spath) + stdin, err := proc.StdinPipe() + if err != nil { + return nil, err + } + defer stdin.Close() + + outb, err := proc.StdoutPipe() + if err != nil { + return nil, err + } + defer outb.Close() + + // errb, err := proc.StderrPipe() + // if err != nil { + // return nil, err + // } + // defer errb.Close() + + if err := proc.Start(); err != nil { + return nil, err + } + keypath := strings.Join(keySeg, "\\") + io.WriteString(stdin, fmt.Sprintf("%s %s\n", verb, keypath)) + io.WriteString(stdin, "q\n") + stdoutPut, err := ioutil.ReadAll(outb) + if err != nil { + return nil, err + } + if !proc.ProcessState.Exited() { + proc.Process.Kill() + } + return strings.Split(string(stdoutPut), "\n"), nil +} + +func (w *SWinRegTool) getRegistry(spath string, keySeg []string) string { + keyPath := strings.Join(keySeg, "\\") + lines, err := w.showRegistry(spath, keySeg, "cat") + if err != nil { + log.Errorln(err) + return "" + } + for i, line := range lines { + if len(keyPath) > 85 { + keyPath = keyPath[:85] + } + if strings.Contains(line, fmt.Sprintf("> Value <%s> of type REG_", keyPath)) { + return lines[i+1] + } + } + return "" +} + +type sRegistry struct { + Key string + Type string + Size string +} + +func (w *SWinRegTool) listRegistry(spath string, keySeg []string) ([]string, []sRegistry, error) { + lines, err := w.showRegistry(spath, keySeg, "ls") + if err != nil { + return nil, nil, err + } + keys := []string{} + values := []sRegistry{} + keyPattern := regexp.MustCompile("^<(?P[^>]+)>$") + valPattern := regexp.MustCompile(`^(?P\d+)\s+(?PREG\_\w+)\s+<(?P[^>]+)>\s*`) + for _, line := range lines { + m := regutils2.GetParams(keyPattern, line) + if len(m) > 0 { + keys = append(keys, m["key"]) + } + m = regutils2.GetParams(valPattern, line) + if len(m) > 0 { + values = append(values, sRegistry{m["key"], m["type"], m["size"]}) + } + } + + return keys, values, nil +} + +func (w *SWinRegTool) cmdRegistry(spath string, ops []string, retcode int) bool { + proc := exec.Command(GetChntpwPath(), "-e", spath) + stdin, err := proc.StdinPipe() + if err != nil { + log.Errorln(err) + return false + } + defer stdin.Close() + + outb, err := proc.StdoutPipe() + if err != nil { + log.Errorln(err) + return false + } + defer outb.Close() + + errb, err := proc.StderrPipe() + if err != nil { + log.Errorln(err) + return false + } + defer errb.Close() + + if err := proc.Start(); err != nil { + log.Errorln(err) + return false + } + + for _, op := range ops { + io.WriteString(stdin, op+"\n") + } + io.WriteString(stdin, "q\n") + io.WriteString(stdin, CONFIRM+"\n") + stdoutPut, err := ioutil.ReadAll(outb) + if err != nil { + log.Errorln(err) + return false + } + stderrOutPut, err := ioutil.ReadAll(errb) + if err != nil { + log.Errorln(err) + return false + } + log.Debugf("Cmd registry %s %s", stdoutPut, stderrOutPut) + if proc.ProcessState.Exited() { + if err := proc.Wait(); err != nil { + if exiterr, ok := err.(*exec.ExitError); ok { + ws := exiterr.Sys().(syscall.WaitStatus) + if ws.ExitStatus() == retcode { + return true + } + } + } else { + return retcode == 0 + } + } else { + proc.Process.Kill() + } + return false +} + +func (w *SWinRegTool) setRegistry(spath string, keySeg []string, value string) bool { + keyPath := strings.Join(keySeg, "\\") + return w.cmdRegistry(spath, []string{fmt.Sprintf("ed %s", keyPath), value}, 0) +} + +func (w *SWinRegTool) mkdir(spath string, keySeg []string) bool { + return w.cmdRegistry(spath, + []string{ + fmt.Sprintf("cd %s", strings.Join(keySeg[:len(keySeg)-1], "\\")), + fmt.Sprintf("nk %s", keySeg[len(keySeg)-1]), + }, 2) +} + +func (w *SWinRegTool) keyExists(spath string, keySeg []string) bool { + keys, _, err := w.listRegistry(spath, keySeg[:len(keySeg)-1]) + if err != nil { + log.Errorln(err) + return false + } + if utils.IsInStringArray(keySeg[len(keySeg)-1], keys) { + return true + } + return false +} + +func (w *SWinRegTool) valExists(spath string, keySeg []string) bool { + _, vals, err := w.listRegistry(spath, keySeg[:len(keySeg)-1]) + if err != nil { + log.Errorln(err) + return false + } + for _, val := range vals { + if val.Key == keySeg[len(keySeg)-1] { + return true + } + } + return false +} + +func (w *SWinRegTool) mkdir_P(spath string, keySeg []string) bool { + seg := []string{} + for _, k := range keySeg { + seg = append(seg, k) + if !w.keyExists(spath, seg) { + if !w.mkdir(spath, seg) { + return false + } + } + } + return true +} + +func (w *SWinRegTool) newValue(spath string, keySeg []string, regtype, val string) bool { + REG_TYPE_TBL := []string{ + "REG_NONE", + "REG_SZ", + "REG_EXPAND_SZ", + "REG_BINARY", + "REG_DWORD", + "REG_DWORD_BIG_ENDIAN", + "REG_LINK", + "REG_MULTI_SZ", + "REG_RESOUCE_LIST", + "REG_FULL_RES_DESC", + "REG_RES_REQ", + "REG_QWORD", + } + + ok, idx := utils.InStringArray(regtype, REG_TYPE_TBL) + if !ok { + return false + } + + cmds := []string{ + fmt.Sprintf("cd %s", strings.Join(keySeg[:len(keySeg)-1], "\\")), + fmt.Sprintf("nv %x %s", idx, keySeg[len(keySeg)-1]), + fmt.Sprintf("ed %s", keySeg[len(keySeg)-1]), + } + + if regtype == "REG_QWORD" { + cmds = append(cmds, "16", ": 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "s") + } else { + cmds = append(cmds, val) + } + return w.cmdRegistry(spath, cmds, 0) +} + +func (w *SWinRegTool) GetRegistry(keyPath string) string { + p1, p2s := w.GetRegFile(keyPath) + if len(p1) == 0 && len(p2s) == 0 { + return "" + } else { + return w.getRegistry(p1, p2s) + } +} + +func (w *SWinRegTool) ListRegistry(keyPath string) ([]string, []sRegistry) { + p1, p2s := w.GetRegFile(keyPath) + if len(p1) == 0 && len(p2s) == 0 { + return nil, nil + } else { + v1, v2, err := w.listRegistry(p1, p2s) + if err != nil { + log.Errorln(err) + return nil, nil + } + return v1, v2 + } +} + +func (w *SWinRegTool) SetRegistry(keyPath, value, regtype string) bool { + p1, p2s := w.GetRegFile(keyPath) + if len(p1) == 0 && len(p2s) == 0 { + return false + } else { + if w.valExists(p1, p2s) { + return w.setRegistry(p1, p2s, value) + } else { + if !w.keyExists(p1, p2s[:len(p2s)-1]) { + if !w.mkdir_P(p1, p2s[:len(p2s)-1]) { + return false + } + } + return w.newValue(p1, p2s, regtype, value) + } + } +} + +func (w *SWinRegTool) KeyExists(keyPath string) bool { + p1, p2s := w.GetRegFile(keyPath) + if len(p1) == 0 && len(p2s) == 0 { + return false + } else { + return w.keyExists(p1, p2s) + } +} + +func (w *SWinRegTool) MkdirP(keyPath string) bool { + p1, p2s := w.GetRegFile(keyPath) + if len(p1) == 0 && len(p2s) == 0 { + return false + } else { + return w.mkdir_P(p1, p2s) + } +} + +func (w *SWinRegTool) GetCcsKey() string { + ver := w.GetRegistry(`HKLM\SYSTEM\Select\Current`) + iv, _ := strconv.ParseInt(ver, 16, 0) + return fmt.Sprintf("ControlSet%03d", iv) +} + +func (w *SWinRegTool) GetCcsKeyPath() string { + return fmt.Sprintf(`HKLM\SYSTEM\%s`, w.GetCcsKey()) +} + +func (w *SWinRegTool) getComputerNameKeyPath() string { + key := w.GetCcsKey() + return key + `\Control\ComputerName\ComputerName\ComputerName` +} + +func (w *SWinRegTool) GetComputerName() string { + key := w.getComputerNameKeyPath() + return w.GetRegistry(key) +} + +func (w *SWinRegTool) setComputerName(cn string) { + MAX_COMPUTER_NAME_LEN := 15 + COMMON_PREFIX_LEN := 10 + if len(cn) > MAX_COMPUTER_NAME_LEN { + suffix := cn[COMMON_PREFIX_LEN:] + suffixlen := MAX_COMPUTER_NAME_LEN - COMMON_PREFIX_LEN + md5sum := md5.Sum([]byte(suffix)) + cn = cn[:COMMON_PREFIX_LEN] + string(md5sum[:])[:suffixlen] + } + key := w.getComputerNameKeyPath() + w.SetRegistry(key, cn, "") +} + +func (w *SWinRegTool) SetHostname(hostname, domain string) { + tcpipKey := w.GetCcsKeyPath() + `\Services\Tcpip\Parameters` + hnKey := tcpipKey + `\Hostname` + dmKey := tcpipKey + `\Domain` + nvHnKey := tcpipKey + `\NV Hostname` + nvDmKey := tcpipKey + `\NV Domain` + w.SetRegistry(hnKey, hostname, "REG_SZ") + w.SetRegistry(dmKey, domain, "REG_SZ") + w.SetRegistry(nvHnKey, hostname, "REG_SZ") + w.SetRegistry(nvDmKey, domain, "REG_SZ") +} + +func (w *SWinRegTool) SetDnsServer(nameserver, searchlist string) { + tcpipKey := w.GetCcsKeyPath() + `\Services\Tcpip\Parameters` + ns_key := tcpipKey + `\NameServer` + search_key := tcpipKey + `\SearchList` + w.SetRegistry(ns_key, nameserver, "REG_SZ") + w.SetRegistry(search_key, searchlist, "REG_SZ") +} + +func (w *SWinRegTool) GetProductName() string { + prodKey := `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductName` + return w.GetRegistry(prodKey) +} + +func (w *SWinRegTool) GetVersion() string { + prodKey := `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentVersion` + return w.GetRegistry(prodKey) +} + +func (w *SWinRegTool) GetInstallLanguage() string { + nlsTbl := map[string]string{"0804": "zh_CN", "0404": "zh_TW", "0c04": "zh_HK", + "1004": "zh_SG", "0409": "en_US", "0809": "en_UK"} + key := w.GetCcsKeyPath() + key += `\Control\Nls\Language\InstallLanguage` + val := w.GetRegistry(key) + if xval, ok := nlsTbl[key]; ok { + return xval + } else { + return val + } +} + +func (w *SWinRegTool) GetArch() string { + prodKey := `HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\CurrentVersion` + ver := w.GetRegistry(prodKey) + if len(ver) > 0 { + return "x86_64" + } else { + return "x86" + } +} + +func (w *SWinRegTool) LogontypePath() string { + return `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\LogonType` +} + +func (w *SWinRegTool) GetLogontype() string { + return w.GetRegistry(w.LogontypePath()) +} + +func (w *SWinRegTool) SetLogontype(val string) { + w.SetRegistry(w.LogontypePath(), val, "") +} + +func (w *SWinRegTool) DefaultAccountPath() string { + return `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultUserName` +} + +func (w *SWinRegTool) GetDefaultAccount() string { + return w.GetRegistry(w.DefaultAccountPath()) +} + +func (w *SWinRegTool) SetDefaultAccount(user string) { + w.SetRegistry(w.DefaultAccountPath(), user, "") +} + +func (w *SWinRegTool) GpeditScriptPath() string { + return `HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts` +} + +func (w *SWinRegTool) GpeditScriptStatePath() string { + return `HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts` +} + +func (w *SWinRegTool) GetGpeditStartScripts() []string { + scriptKey := w.GpeditScriptPath() + `\Startup\0` + keys, _ := w.ListRegistry(scriptKey) + ret := []string{} + for _, k := range keys { + spath := scriptKey + (fmt.Sprintf(`\%s\Script`, k)) + val := w.GetRegistry(spath) + ret = append(ret, val) + } + return ret +} + +func (w *SWinRegTool) IsGpeditStartScriptInstalled(script string) bool { + scripts := w.GetGpeditStartScripts() + return utils.IsInStringArray(script, scripts) +} + +func (w *SWinRegTool) InstallGpeditStartScript(script string) { + if w.IsGpeditStartScriptInstalled(script) { + return + } + w.installGpeditStartScript(script, w.GpeditScriptPath()) + w.installGpeditStartScript(script, w.GpeditScriptStatePath()) +} + +func (w *SWinRegTool) GetGpoDisplayname() string { + spath := `HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\GPO-List\0\DisplayName` + return w.GetRegistry(spath) +} + +func (w *SWinRegTool) installGpeditStartScript(script, scriptPath string) { + idx := 0 + if !w.KeyExists(scriptPath + `\Startup`) { + w.MkdirP(scriptPath + `\Startup`) + w.MkdirP(scriptPath + `\Shutdown`) + dsname := "Local Group Policy" + kvts := [][3]string{ + [3]string{"GPO-ID", "LocalGPO", "REG_SZ"}, + [3]string{"SOM-ID", "Local", "REG_SZ"}, + [3]string{"FileSysPath", `C:\Windows\System32\GroupPolicy\Machine`, "REG_SZ"}, + [3]string{"DisplayName", dsname, "REG_SZ"}, + [3]string{"GPOName", dsname, "REG_SZ"}, + [3]string{"PSScriptOrder", "1", "REG_DWORD"}, + } + for _, kvt := range kvts { + w.SetRegistry(fmt.Sprintf(`%s\Startup\0\%s`, scriptPath, kvt[0]), kvt[1], kvt[2]) + } + + } else { + for w.KeyExists(scriptPath + (fmt.Sprintf(`\Startup\0\%d`, idx))) { + idx += 1 + } + } + + kvts := [][3]string{ + [3]string{"Script", script, "REG_SZ"}, + [3]string{"Parameters", "", "REG_SZ"}, + [3]string{"ExecTime", "", "REG_QWORD"}, + [3]string{"IsPowershell", "0", "REG_DWORD"}, + } + for _, kvt := range kvts { + w.SetRegistry(fmt.Sprintf(`%s\Startup\0\%d\%s`, scriptPath, idx, kvt[0]), kvt[1], kvt[2]) + } +} + +func (w *SWinRegTool) EnableRdp() { + key := w.GetCcsKeyPath() + `\Control\Terminal Server\fDenyTSConnections` + w.SetRegistry(key, "0", `REG_DWORD`) + key = w.GetCcsKeyPath() + `\Services\MpsSvc\Start` + w.SetRegistry(key, "3", `REG_DWORD`) +}