os/user: report an unknown Windows account as UnknownUserError Lookup documents that a user who cannot be found is reported as UnknownUserError, and LookupGroup says the same for UnknownGroupError. On Windows both resolve names through LookupAccountName, which fails with ERROR_NONE_MAPPED when a name maps to no account at all, and that raw syscall.Errno reached the caller unchanged. Map it at the two name lookup sites. The Windows error is wrapped rather than replaced so that callers already testing for ERROR_NONE_MAPPED keep matching it. Nothing on Windows returned these types before, so no existing result changes. One difference from the other platforms is worth noting: they return the bare error type, while this returns a wrapper, so errors.As finds it and a direct type assertion does not. Fixes #73595 Change-Id: I6f38d47baca0fee26f62894aff1b676ff5b3b0df Reviewed-on: https://go-review.googlesource.com/c/go/+/819062 Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Quim Muntal <quimmuntal@gmail.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go index a6806c3..286a597 100644 --- a/src/internal/syscall/windows/syscall_windows.go +++ b/src/internal/syscall/windows/syscall_windows.go
@@ -44,6 +44,7 @@ ERROR_IO_INCOMPLETE syscall.Errno = 996 ERROR_NO_TOKEN syscall.Errno = 1008 ERROR_NO_UNICODE_TRANSLATION syscall.Errno = 1113 + ERROR_NONE_MAPPED syscall.Errno = 1332 ERROR_CANT_ACCESS_FILE syscall.Errno = 1920 )
diff --git a/src/os/user/lookup_windows.go b/src/os/user/lookup_windows.go index e0e77f3..1d71ca4 100644 --- a/src/os/user/lookup_windows.go +++ b/src/os/user/lookup_windows.go
@@ -173,6 +173,9 @@ func lookupGroupName(groupname string) (string, error) { sid, _, t, e := syscall.LookupSID("", groupname) if e != nil { + if errors.Is(e, windows.ERROR_NONE_MAPPED) { + return "", fmt.Errorf("%w: %w", UnknownGroupError(groupname), e) + } return "", e } if !isValidGroupAccountType(t) { @@ -454,6 +457,9 @@ func lookupUser(username string) (*User, error) { sid, _, t, e := syscall.LookupSID("", username) if e != nil { + if errors.Is(e, windows.ERROR_NONE_MAPPED) { + return nil, fmt.Errorf("%w: %w", UnknownUserError(username), e) + } return nil, e } if !isValidUserAccountType(sid, t) {
diff --git a/src/os/user/user_windows_test.go b/src/os/user/user_windows_test.go index c958115..d6f25ed 100644 --- a/src/os/user/user_windows_test.go +++ b/src/os/user/user_windows_test.go
@@ -358,3 +358,34 @@ } } } + +func TestLookupUnknownName(t *testing.T) { + // A name that resolves to no account makes LookupAccountName fail + // with ERROR_NONE_MAPPED. That is the case Lookup and LookupGroup + // document as UnknownUserError and UnknownGroupError. + const name = "go-test-no-such-account-4a9f2c" + + if _, err := Lookup(name); err == nil { + t.Errorf("Lookup(%q) = nil error, want UnknownUserError", name) + } else { + var unknown UnknownUserError + if !errors.As(err, &unknown) { + t.Errorf("Lookup(%q) error = %v (%T), want UnknownUserError", name, err, err) + } + if !errors.Is(err, windows.ERROR_NONE_MAPPED) { + t.Errorf("Lookup(%q) error = %v, want it to wrap ERROR_NONE_MAPPED", name, err) + } + } + + if _, err := LookupGroup(name); err == nil { + t.Errorf("LookupGroup(%q) = nil error, want UnknownGroupError", name) + } else { + var unknown UnknownGroupError + if !errors.As(err, &unknown) { + t.Errorf("LookupGroup(%q) error = %v (%T), want UnknownGroupError", name, err, err) + } + if !errors.Is(err, windows.ERROR_NONE_MAPPED) { + t.Errorf("LookupGroup(%q) error = %v, want it to wrap ERROR_NONE_MAPPED", name, err) + } + } +}