Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

os/user: On windows, current() -> t.GetUserProfileDirectory() errors when for RemoteInteractive Logon session on system without user profile #37348

Open
khyberspache opened this issue Feb 21, 2020 · 13 comments

Comments

@khyberspache
Copy link

@khyberspache khyberspache commented Feb 21, 2020

What version of Go are you using (go version)?

$ go version
go version go1.13.7 darwin/amd64

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/amanners/Library/Caches/go-build"
GOENV="/Users/amanners/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/amanners/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/Cellar/go/1.13.7/libexec"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.13.7/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/5l/sgy31cm96k9671825xjz_sm40000gq/T/go-build492878366=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

Attempted to use WMIC/PSRemote to interact with a system to launch a GO executable that logs the current user (aka PSSession user/WMI exec) using user.Current(). User returns as nil with err The system cannot find the file specified. because the user does not have a home directory on the remote system.

Platform: Windows Server 2016 Version 1607 Build 14393.3326

What did you expect to see?

Valid User structure with Dir as nil/empty string.

os/user/lookup_windows.go:184

func newUser(uid, gid, dir, username, domain string) (*User, error) {
	domainAndUser := domain + `\` + username
	name, e := lookupFullName(domain, username, domainAndUser)
	if e != nil {
		return nil, e
	}
	u := &User{
		Uid:      uid,
		Gid:      gid,
		Username: domainAndUser,
		Name:     name,
		HomeDir:  dir, <- should return as empty/nil 
	}
	return u, nil
}

What did you see instead?

No user returned, error stated above.

Extra information

The specific issue resides in how t.GetUserProfileDirectory() is handled in current():

func current() (*User, error) {
	t, e := syscall.OpenCurrentProcessToken()
	if e != nil {
		return nil, e
	}
	defer t.Close()
	u, e := t.GetTokenUser()
	if e != nil {
		return nil, e
	}
	pg, e := t.GetTokenPrimaryGroup()
	if e != nil {
		return nil, e
	}
	uid, e := u.User.Sid.String()
	if e != nil {
		return nil, e
	}
	gid, e := pg.PrimaryGroup.String()
	if e != nil {
		return nil, e
	}
	dir, e := t.GetUserProfileDirectory() <- These lines
	if e != nil {
		return nil, e <- should set directory to nil and continue on
	}
	username, domain, e := lookupUsernameAndDomain(u.User.Sid)
	if e != nil {
		return nil, e
	}
	return newUser(uid, gid, dir, username, domain)
}

In windows environments it is possible to have a login type where the user doesn't have a Home Directory on that system (all remote windows domain administrator tasks). I will submit a PR with a proposed solution to this.

@christophert and I discovered this while trying to start remote tasks on Windows system that we had never logged into.

@mattn
Copy link
Member

@mattn mattn commented Feb 21, 2020

Could you please update issue detail to describe version or conditions your Windows OS? And #30102 seems not depend on this issue.

@khyberspache
Copy link
Author

@khyberspache khyberspache commented Feb 21, 2020

Mistake on adding the issue number, @christophert can you send OS Version & Release information.

@christophert
Copy link

@christophert christophert commented Feb 21, 2020

@mattn We're running Windows Server 2016 Version 1607 Build 14393.3326. The error we get back from user.Current() is The system cannot find the file specified.

@mattn
Copy link
Member

@mattn mattn commented Feb 21, 2020

Could you please confirm that following key exists on the remote server?

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\XXXXXXXXXXXX\ProfilesDirectory

You can get UID with using simple code.

package main

import (
	"fmt"
	"os/user"
)

func main() {
	u, _ := user.Current()
	fmt.Println(u.Uid)
}
@christophert
Copy link

@christophert christophert commented Feb 21, 2020

@mattn The key does not exist when you PSRemote into a machine you haven't interactively logged into before. The code snippet you posted will fail because the call to Current() will return nil, e on trying to get the user profile directory.

@toothrot toothrot changed the title src/os/user/lookup_windows.go: current() -> t.GetUserProfileDirectory() errors when for RemoteInteractive Logon session on system without user profile os/user: On windows, current() -> t.GetUserProfileDirectory() errors when for RemoteInteractive Logon session on system without user profile Feb 21, 2020
@toothrot toothrot added this to the Backlog milestone Feb 21, 2020
@toothrot
Copy link
Contributor

@toothrot toothrot commented Feb 21, 2020

/cc @bradfitz

@networkimprov
Copy link

@networkimprov networkimprov commented Feb 22, 2020

@gopherbot add OS-Windows

cc @alexbrainman @zx2c4

@alexbrainman
Copy link
Member

@alexbrainman alexbrainman commented Mar 21, 2020

cc @alexbrainman @zx2c4

I am not security expert. I wouldn't trust myself making any decisions here. Sorry.

Maybe Jason have some opinion about this.

Alex

@zx2c4
Copy link
Contributor

@zx2c4 zx2c4 commented Mar 21, 2020

Looks like the issue is that sometimes current() is unable to get the user profile directory but is able to get the other information. @khyberspache's proposal appears to be to just make getting the user profile directory non-fatal, and having that be a nil field inside the returned struct. That seems probably fine.

@khyberspache
Copy link
Author

@khyberspache khyberspache commented Mar 21, 2020

Something like this would allow for a nil dir value when we encounter the use-case outlined in the issue (I think? - you guys are the experts haha):

dir, e := t.GetUserProfileDirectory()
if e != nil && e != syscall.ERROR_FILE_NOT_FOUND {
	return nil, e
}
@alexbrainman
Copy link
Member

@alexbrainman alexbrainman commented Mar 22, 2020

dir, e := t.GetUserProfileDirectory()
if e != nil && e != syscall.ERROR_FILE_NOT_FOUND {
	return nil, e
}

What happens, if home disrectlry exists and t.GetUserProfileDirectory returns syscall.ERROR_FILE_NOT_FOUND for a reason?

Alex

@khyberspache
Copy link
Author

@khyberspache khyberspache commented Mar 23, 2020

I'd have to dig into the implementation of GetUserProfileDirectory() to give a good answer to that. Alternatively, instead of returning, you could just make dir = nil on any error, but that's probably not an ideal solution:

dir, e := t.GetUserProfileDirectory()
if e !=nil {
    dir = nil
}
@alexbrainman
Copy link
Member

@alexbrainman alexbrainman commented Mar 25, 2020

Alternatively, instead of returning, you could just make dir = nil on any error, but that's probably not an ideal solution:

I am not sure that is better than your original proposal. And I don't have good suggestions. Leaving for others to decide.

Alex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
8 participants
You can’t perform that action at this time.