google_guest_agent/accounts_windows.go (152 lines of code) (raw):
// Copyright 2017 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"fmt"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var (
netAPI32 = windows.NewLazySystemDLL("netapi32.dll")
procNetUserAdd = netAPI32.NewProc("NetUserAdd")
procNetUserGetInfo = netAPI32.NewProc("NetUserGetInfo")
procNetUserSetInfo = netAPI32.NewProc("NetUserSetInfo")
procNetLocalGroupAddMembers = netAPI32.NewProc("NetLocalGroupAddMembers")
)
type (
USER_INFO_0 struct {
Usri0_name LPWSTR
}
USER_INFO_1 struct {
Usri1_name LPWSTR
Usri1_password LPWSTR
Usri1_password_age DWORD
Usri1_priv DWORD
Usri1_home_dir LPWSTR
Usri1_comment LPWSTR
Usri1_flags DWORD
Usri1_script_path LPWSTR
}
LOCALGROUP_MEMBERS_INFO_0 struct {
Lgrmi0_sid *syscall.SID
}
USER_INFO_1003 struct {
Usri1003_password LPWSTR
}
)
const (
USER_PRIV_GUEST = 0
USER_PRIV_USER = 1
USER_PRIV_ADMIN = 2
UF_SCRIPT = 0x0001
UF_ACCOUNTDISABLE = 0x0002
UF_HOMEDIR_REQUIRED = 0x0008
UF_LOCKOUT = 0x0010
UF_PASSWD_NOTREQD = 0x0020
UF_PASSWD_CANT_CHANGE = 0x0040
UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 0x0080
UF_TEMP_DUPLICATE_ACCOUNT = 0x0100
UF_NORMAL_ACCOUNT = 0x0200
UF_INTERDOMAIN_TRUST_ACCOUNT = 0x0800
UF_WORKSTATION_TRUST_ACCOUNT = 0x1000
UF_SERVER_TRUST_ACCOUNT = 0x2000
UF_DONT_EXPIRE_PASSWD = 0x10000
UF_MNS_LOGON_ACCOUNT = 0x20000
UF_SMARTCARD_REQUIRED = 0x40000
UF_TRUSTED_FOR_DELEGATION = 0x80000
UF_NOT_DELEGATED = 0x100000
UF_USE_DES_KEY_ONLY = 0x200000
UF_DONT_REQUIRE_PREAUTH = 0x400000
UF_PASSWORD_EXPIRED = 0x800000
UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 0x1000000
UF_NO_AUTH_DATA_REQUIRED = 0x2000000
UF_PARTIAL_SECRETS_ACCOUNT = 0x4000000
UF_USE_AES_KEYS = 0x8000000
)
func resetPwd(username, pwd string) error {
uPtr, err := syscall.UTF16PtrFromString(username)
if err != nil {
return fmt.Errorf("error encoding username to UTF16: %v", err)
}
pPtr, err := syscall.UTF16PtrFromString(pwd)
if err != nil {
return fmt.Errorf("error encoding password to UTF16: %v", err)
}
ret, _, _ := procNetUserSetInfo.Call(
uintptr(0),
uintptr(unsafe.Pointer(uPtr)),
uintptr(1003),
uintptr(unsafe.Pointer(&USER_INFO_1003{pPtr})),
uintptr(0))
if ret != 0 {
return fmt.Errorf("nonzero return code from NetUserSetInfo: %s", syscall.Errno(ret))
}
return nil
}
func addUserToGroup(_ context.Context, username, group string) error {
gPtr, err := syscall.UTF16PtrFromString(group)
if err != nil {
return fmt.Errorf("error encoding group to UTF16: %v", err)
}
sid, _, _, err := syscall.LookupSID("", username)
if err != nil {
return err
}
sArray := []LOCALGROUP_MEMBERS_INFO_0{{sid}}
ret, _, _ := procNetLocalGroupAddMembers.Call(
uintptr(0),
uintptr(unsafe.Pointer(gPtr)),
uintptr(0),
uintptr(unsafe.Pointer(&sArray[0])),
uintptr(1),
)
// Ignore ERROR_MEMBER_IN_ALIAS (1378).
if ret != 0 && ret != 1378 {
return fmt.Errorf("nonzero return code from NetLocalGroupAddMembers: %s", syscall.Errno(ret))
}
return nil
}
func createUser(_ context.Context, username, pwd, _ string) error {
uPtr, err := syscall.UTF16PtrFromString(username)
if err != nil {
return fmt.Errorf("error encoding username to UTF16: %v", err)
}
pPtr, err := syscall.UTF16PtrFromString(pwd)
if err != nil {
return fmt.Errorf("error encoding password to UTF16: %v", err)
}
uInfo1 := USER_INFO_1{
Usri1_name: uPtr,
Usri1_password: pPtr,
Usri1_priv: USER_PRIV_USER,
Usri1_flags: UF_SCRIPT | UF_NORMAL_ACCOUNT | UF_DONT_EXPIRE_PASSWD,
}
ret, _, _ := procNetUserAdd.Call(
uintptr(0),
uintptr(1),
uintptr(unsafe.Pointer(&uInfo1)),
uintptr(0),
)
// If Error 2236 = The user already belongs to this group.
// No action is required, see:
// https://learn.microsoft.com/en-us/troubleshoot/windows-server/remote/terminal-server-error-messages-2200-to-2299#error-2236
if ret != 0 && ret != 2236 {
return fmt.Errorf("nonzero return code from NetUserAdd: %s", syscall.Errno(ret))
}
return nil
}
func userExists(name string) (bool, error) {
uPtr, err := syscall.UTF16PtrFromString(name)
if err != nil {
return false, fmt.Errorf("error encoding username to UTF16: %v", err)
}
ret, _, _ := procNetUserGetInfo.Call(
uintptr(0),
uintptr(unsafe.Pointer(uPtr)),
uintptr(1),
uintptr(unsafe.Pointer(&USER_INFO_0{})),
)
if ret != 0 {
return false, fmt.Errorf("nonzero return code from NetUserGetInfo: %s", syscall.Errno(ret))
}
return true, nil
}
func getUIDAndGID(_ string) (string, string) {
return "", ""
}