Source file src/os/user/user_windows_test.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package user
     6  
     7  import (
     8  	"crypto/rand"
     9  	"encoding/base64"
    10  	"errors"
    11  	"fmt"
    12  	"internal/syscall/windows"
    13  	"internal/testenv"
    14  	"os"
    15  	"os/exec"
    16  	"runtime"
    17  	"strconv"
    18  	"syscall"
    19  	"testing"
    20  	"unsafe"
    21  )
    22  
    23  // windowsTestAcount creates a test user and returns a token for that user.
    24  // If the user already exists, it will be deleted and recreated.
    25  // The caller is responsible for closing the token.
    26  func windowsTestAcount(t *testing.T) (syscall.Token, *User) {
    27  	const testUserName = "GoStdTestUser01"
    28  	var password [33]byte
    29  	rand.Read(password[:])
    30  	// Add special chars to ensure it satisfies password requirements.
    31  	pwd := base64.StdEncoding.EncodeToString(password[:]) + "_-As@!%*(1)4#2"
    32  	name, err := syscall.UTF16PtrFromString(testUserName)
    33  	if err != nil {
    34  		t.Fatal(err)
    35  	}
    36  	pwd16, err := syscall.UTF16PtrFromString(pwd)
    37  	if err != nil {
    38  		t.Fatal(err)
    39  	}
    40  	userInfo := windows.UserInfo1{
    41  		Name:     name,
    42  		Password: pwd16,
    43  		Priv:     windows.USER_PRIV_USER,
    44  	}
    45  	// Create user.
    46  	err = windows.NetUserAdd(nil, 1, (*byte)(unsafe.Pointer(&userInfo)), nil)
    47  	if errors.Is(err, syscall.ERROR_ACCESS_DENIED) {
    48  		t.Skip("skipping test; don't have permission to create user")
    49  	}
    50  	if errors.Is(err, windows.NERR_UserExists) {
    51  		// User already exists, delete and recreate.
    52  		if err = windows.NetUserDel(nil, name); err != nil {
    53  			t.Fatal(err)
    54  		}
    55  		if err = windows.NetUserAdd(nil, 1, (*byte)(unsafe.Pointer(&userInfo)), nil); err != nil {
    56  			t.Fatal(err)
    57  		}
    58  	} else if err != nil {
    59  		t.Fatal(err)
    60  	}
    61  	t.Cleanup(func() {
    62  		if err = windows.NetUserDel(nil, name); err != nil {
    63  			if !errors.Is(err, windows.NERR_UserNotFound) {
    64  				t.Fatal(err)
    65  			}
    66  		}
    67  	})
    68  	domain, err := syscall.UTF16PtrFromString(".")
    69  	if err != nil {
    70  		t.Fatal(err)
    71  	}
    72  	const LOGON32_PROVIDER_DEFAULT = 0
    73  	const LOGON32_LOGON_INTERACTIVE = 2
    74  	var token syscall.Token
    75  	if err = windows.LogonUser(name, domain, pwd16, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &token); err != nil {
    76  		t.Fatal(err)
    77  	}
    78  	t.Cleanup(func() {
    79  		token.Close()
    80  	})
    81  	usr, err := Lookup(testUserName)
    82  	if err != nil {
    83  		t.Fatal(err)
    84  	}
    85  	return token, usr
    86  }
    87  
    88  func TestImpersonatedSelf(t *testing.T) {
    89  	runtime.LockOSThread()
    90  	defer runtime.UnlockOSThread()
    91  
    92  	want, err := current()
    93  	if err != nil {
    94  		t.Fatal(err)
    95  	}
    96  
    97  	levels := []uint32{
    98  		windows.SecurityAnonymous,
    99  		windows.SecurityIdentification,
   100  		windows.SecurityImpersonation,
   101  		windows.SecurityDelegation,
   102  	}
   103  	for _, level := range levels {
   104  		t.Run(strconv.Itoa(int(level)), func(t *testing.T) {
   105  			if err = windows.ImpersonateSelf(level); err != nil {
   106  				t.Fatal(err)
   107  			}
   108  			defer windows.RevertToSelf()
   109  
   110  			got, err := current()
   111  			if level == windows.SecurityAnonymous {
   112  				// We can't get the process token when using an anonymous token,
   113  				// so we expect an error here.
   114  				if err == nil {
   115  					t.Fatal("expected error")
   116  				}
   117  				return
   118  			}
   119  			if err != nil {
   120  				t.Fatal(err)
   121  			}
   122  			compare(t, want, got)
   123  		})
   124  	}
   125  }
   126  
   127  func TestImpersonated(t *testing.T) {
   128  	runtime.LockOSThread()
   129  	defer runtime.UnlockOSThread()
   130  
   131  	want, err := current()
   132  	if err != nil {
   133  		t.Fatal(err)
   134  	}
   135  
   136  	// Create a test user and log in as that user.
   137  	token, _ := windowsTestAcount(t)
   138  
   139  	// Impersonate the test user.
   140  	if err = windows.ImpersonateLoggedOnUser(token); err != nil {
   141  		t.Fatal(err)
   142  	}
   143  	defer func() {
   144  		err = windows.RevertToSelf()
   145  		if err != nil {
   146  			// If we can't revert to self, we can't continue testing.
   147  			panic(err)
   148  		}
   149  	}()
   150  
   151  	got, err := current()
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  	compare(t, want, got)
   156  }
   157  
   158  func TestCurrentNetapi32(t *testing.T) {
   159  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   160  		// Test that Current does not load netapi32.dll.
   161  		// First call Current.
   162  		Current()
   163  
   164  		// Then check if netapi32.dll is loaded.
   165  		netapi32, err := syscall.UTF16PtrFromString("netapi32.dll")
   166  		if err != nil {
   167  			fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
   168  			os.Exit(9)
   169  			return
   170  		}
   171  		mod, _ := windows.GetModuleHandle(netapi32)
   172  		if mod != 0 {
   173  			fmt.Fprintf(os.Stderr, "netapi32.dll is loaded\n")
   174  			os.Exit(9)
   175  			return
   176  		}
   177  		os.Exit(0)
   178  		return
   179  	}
   180  	exe := testenv.Executable(t)
   181  	cmd := testenv.CleanCmdEnv(exec.Command(exe, "-test.run=^TestCurrentNetapi32$"))
   182  	cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
   183  	out, err := cmd.CombinedOutput()
   184  	if err != nil {
   185  		t.Fatalf("%v\n%s", err, out)
   186  	}
   187  }
   188  
   189  func TestGroupIdsTestUser(t *testing.T) {
   190  	// Create a test user and log in as that user.
   191  	_, user := windowsTestAcount(t)
   192  
   193  	gids, err := user.GroupIds()
   194  	if err != nil {
   195  		t.Fatal(err)
   196  	}
   197  
   198  	if err != nil {
   199  		t.Fatalf("%+v.GroupIds(): %v", user, err)
   200  	}
   201  	if !containsID(gids, user.Gid) {
   202  		t.Errorf("%+v.GroupIds() = %v; does not contain user GID %s", user, gids, user.Gid)
   203  	}
   204  }
   205  

View as plain text