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: LookupUser() doesn't find users on macOS when compiled with CGO_ENABLED=0 #24383

Open
jeffreydwalter opened this issue Mar 14, 2018 · 23 comments

Comments

@jeffreydwalter
Copy link

@jeffreydwalter jeffreydwalter commented Mar 14, 2018

Please answer these questions before submitting your issue. Thanks!

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

go version go1.9.2 darwin/amd64

Does this issue reproduce with the latest release?

Yes.

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

GOARCH="amd64"
GOBIN=""
GOCACHE="/home/jeff_walter/.cache/go-build"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="freebsd"
GOOS="darwin"
GOPATH="/home/jeff_walter/src/agent"
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/freebsd_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="0"
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 -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build053729832=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

If I run the following program with sudo I get a different result than I get when I run it as 'someuser' (where 'someuser' is my current logged in user).

package main

import (
    "fmt"
    "os/user"
)

func main() {
    u, err := user.Lookup("someuser")
    if err != nil {
        fmt.Printf("%s", err)
        return
    }

    fmt.Printf("%V", u)
}

What did you expect to see?

$ ./user
&{%!V(string=502) %!V(string=20) %!V(string=someuser) %!V(string=) %!V(string=/Users/someuser)}

What did you see instead?

$ sudo ./user
user: unknown user someuser

@jeffreydwalter jeffreydwalter changed the title "os/user" LookupUser() doesn't find user when run as root. os/user: LookupUser() doesn't find user when run as root. Mar 14, 2018
@odeke-em
Copy link
Member

@odeke-em odeke-em commented Mar 14, 2018

Hello @jeffreydwalter, I am not sure what you were expecting to see but when running as root, you are a different user entirely so hardcoding the username wouldn't work unless you actually used "root"

Here is an update of your repro that'll get you the information from the environment whether ran as root or as an ordinary user, it uses os.ExpandEnv("$USER")

package main

import (
    "fmt"
    "os"
    "os/user"
)

func main() {
    u, err := user.Lookup(os.ExpandEnv("$USER"))
    if err != nil {
        fmt.Printf("%s", err)
        return
    }

    fmt.Printf("%+v\n", u)
}

Ran as a normal user

e$ go run main.go 
&{Uid:501 Gid:20 Username:emmanuelodeke Name:Emmanuel Odeke HomeDir:/Users/emmanuelodeke}

Ran as root

sudo su && go run main.go
&{Uid:0 Gid:0 Username:root Name:System Administrator HomeDir:/var/root}

Hope this helps.

@jeffreydwalter
Copy link
Author

@jeffreydwalter jeffreydwalter commented Mar 14, 2018

@mattn
Copy link
Member

@mattn mattn commented Mar 14, 2018

please use user.Current()

https://golang.org/pkg/os/user/#Current

@odeke-em
Copy link
Member

@odeke-em odeke-em commented Mar 14, 2018

What expecting is to get back the user info for the username I passed in,
regardless of which user I'm running as.

Gotcha, sorry @jeffreydwalter, I misunderstood your intention. Okay, re-running your original repro but searching for "emmanuelodeke" my user, even as root gives me the correct output

&{%!V(string=501) %!V(string=20) %!V(string=emmanuelodeke) %!V(string=Emmanuel Odeke) %!V(string=/Users/emmanuelodeke)}

or with %V fixed to %+v

sh-3.2# go run main.go 
&user.User{Uid:"501", Gid:"20", Username:"emmanuelodeke", Name:"Emmanuel Odeke", HomeDir:"/Users/emmanuelodeke"}
@odeke-em
Copy link
Member

@odeke-em odeke-em commented Mar 14, 2018

And vice versa, searching for root as my ordinary user gives me the correct output
screen shot 2018-03-13 at 6 49 37 pm

@jeffreydwalter
Copy link
Author

@jeffreydwalter jeffreydwalter commented Mar 14, 2018

Thanks @odeke-em,

Are you running on macOS? Did you run as root?

@jeffreydwalter
Copy link
Author

@jeffreydwalter jeffreydwalter commented Mar 14, 2018

Interesting... I wonder if it has to do with the underscore in my username?

@jeffreydwalter
Copy link
Author

@jeffreydwalter jeffreydwalter commented Mar 14, 2018

@odeke-em, so there must be something with my user. If I lookup root, everything works as it does for you.

$ ./user 
&{%!V(string=0) %!V(string=0) %!V(string=root) %!V(string=System Administrator) %!V(string=/var/root)}
🍺  [585] jeff_walter@T2000:~/Code
$ sudo ./user 
&{%!V(string=0) %!V(string=0) %!V(string=root) %!V(string=) %!V(string=/Users/jeff_walter)}
🍺  [586] jeff_walter@T2000:~/Code
@jeffreydwalter
Copy link
Author

@jeffreydwalter jeffreydwalter commented Mar 14, 2018

I tried creating a new user, same issue as my existing user. :/

@odeke-em
Copy link
Member

@odeke-em odeke-em commented Mar 14, 2018

@jeffreydwalter interesting, no dice in a new shell? How about with other users on there? Without underscores either?

@jeffreydwalter
Copy link
Author

@jeffreydwalter jeffreydwalter commented Mar 14, 2018

@odeke-em no dice... :/ I downloaded iTerm and still no luck. The only things I haven't tried are to log out or reboot.

I created a user without underscores, named user, same stuff:

$ ./user user
Current user: &{502 20 jeff_walter  /Users/jeff_walter}
Looking up username: user
user: unknown user user

$ sudo ./user user
Current user: &{0 0 root  /Users/jeff_walter}
Looking up username: user
user: unknown user user

$ sudo su - user
T2000:~ user$ ./user user
Current user: &{503 20 user  /Users/user}
Looking up username: user
&{503 20 user  /Users/user}T2000:~ user$ exit
logout

$ sudo su -
T2000:~ root# /Users/user/user user
Current user: &{0 0 root  /var/root}
Looking up username: user

This is really baffling to me... :)

@crvv
Copy link
Contributor

@crvv crvv commented Mar 14, 2018

There are two implementations of user.Lookup.

  1. Parse /etc/passwd
  2. Use getpwnam_r in libc

I can't find my normal user in /etc/passwd on macOS, but root is in it.
With export CGO_ENABLED=0, I can reproduce this bug.

@odeke-em
Copy link
Member

@odeke-em odeke-em commented Mar 14, 2018

@crvv on mine with CGO_ENABLED=0 or with CGO_ENABLED=1, I get the same results: it works for me.

/cc @kevinburke @ianlancetaylor @bradfitz

@agnivade
Copy link
Contributor

@agnivade agnivade commented Mar 14, 2018

I am on linux/amd64 (Ubuntu 16.04). Unable to reproduce. With both CGO_ENABLED=0 and CGO_ENABLED=1.

@crvv
Copy link
Contributor

@crvv crvv commented Mar 14, 2018

@odeke-em
What is your OS version?
Can you find the normal user in /etc/passwd?

@jeffreydwalter
Copy link
Author

@jeffreydwalter jeffreydwalter commented Mar 14, 2018

I do have GCO_ENABLED=0 set. I am only able to reproduce this issue on macOS.

@odeke-em
Copy link
Member

@odeke-em odeke-em commented Mar 14, 2018

@crvv

What is your OS version?

MacOS

Darwin Emmanuels-MacBook-Pro-2.local 15.6.0 Darwin Kernel Version 15.6.0: Sun Jun  4 21:43:07 PDT 2017; root:xnu-3248.70.3~1/RELEASE_X86_64 x86_64

Can you find the normal user in /etc/passwd?

No, I can't

cat /etc/passwd | grep -i emm

produces nothing

@jeffreydwalter
Copy link
Author

@jeffreydwalter jeffreydwalter commented Mar 14, 2018

Apparently, macOS doesn't really use the /etc/passwd file anymore. Apparently, you can get the user info by parsing some plist files.

See https://apple.stackexchange.com/a/186899

$ sudo defaults read /var/db/dslocal/nodes/Default/users/user.plist uid
(
    503
)

$ sudo defaults read /var/db/dslocal/nodes/Default/users/user.plist gid
(
    20
)

$ sudo defaults read /var/db/dslocal/nodes/Default/users/user.plist passwd
(
    "********"
)

$ sudo defaults read /var/db/dslocal/nodes/Default/users/user.plist name
(
    user
)
@jeffreydwalter
Copy link
Author

@jeffreydwalter jeffreydwalter commented Mar 14, 2018

Just confirmed that everything works fine if I compile with CGO_ENABLED=1. So, seems like parsing /etc/passwd is no bueno on macOS.

@jeffreydwalter
Copy link
Author

@jeffreydwalter jeffreydwalter commented Mar 14, 2018

So, to recap. Compiling with GCO_ENABLED=0 and calling user.Lookup() on a user other than yourself or root fails.

The following commands were run as jeff_walter:

$ ./user jeff_walter
Current user: &{502 20 jeff_walter  /Users/jeff_walter}
Looking up username: jeff_walter
&{502 20 jeff_walter  /Users/jeff_walter}

$ sudo ./user jeff_walter
Current user: &{0 0 root  /Users/jeff_walter}
Looking up username: jeff_walter
user: unknown user jeff_walter

$ ./user user
Current user: &{502 20 jeff_walter  /Users/jeff_walter}
Looking up username: user
user: unknown user user

$ sudo ./user user
Current user: &{0 0 root  /Users/jeff_walter}
Looking up username: user
user: unknown user user

$ ./user root
Current user: &{502 20 jeff_walter  /Users/jeff_walter}
Looking up username: root
&{0 0 root System Administrator /var/root}

$ sudo ./user root
Current user: &{0 0 root  /Users/jeff_walter}
Looking up username: root
&{0 0 root  /Users/jeff_walter} <--- THIS ALSO LOOKS ODD, BUT I GUESS SORT OF MAKES SENSE. STILL SEEMS UNEXPECTED.

The following commands were run as root:

$ ./user root
Current user: &{0 0 root  /var/root}
Looking up username: root
&{0 0 root  /var/root}

$ ./user jeff_walter
Current user: &{0 0 root  /var/root}
Looking up username: jeff_walter
user: unknown user jeff_walter
@jeffreydwalter jeffreydwalter changed the title os/user: LookupUser() doesn't find user when run as root. os/user: LookupUser() doesn't find users on macOS when compiled with GCO_ENABLED=0 Mar 14, 2018
@andybons andybons added this to the Unplanned milestone Mar 26, 2018
@andybons
Copy link
Member

@andybons andybons commented Mar 26, 2018

Compiling your program and running (both as root and as my normal username) with CGO_ENABLED=1 and CGO_ENABLED=0 has the same exact output for me on macOS High Sierra 10.13.3:

package main

import (
	"fmt"
	"os"
	"os/user"
)

func main() {
	_, err := user.Lookup(os.Args[1])
	if err != nil {
		fmt.Printf("%v\n", err)
		return
	}
	fmt.Println("found user")
}
$ whoami
andybons
$ CGO_ENABLED=0 go build -o user user.go && ./user rando
user: unknown user rando
$ CGO_ENABLED=0 go build -o user user.go && ./user andybons
found user
$ CGO_ENABLED=1 go build -o user user.go && ./user rando
user: unknown user rando
$ CGO_ENABLED=1 go build -o user user.go && ./user andybons
found user
@bradfitz bradfitz changed the title os/user: LookupUser() doesn't find users on macOS when compiled with GCO_ENABLED=0 os/user: LookupUser() doesn't find users on macOS when compiled with CGO_ENABLED=0 Mar 26, 2018
@bradfitz bradfitz added the OS-Darwin label Mar 26, 2018
@jeffreydwalter
Copy link
Author

@jeffreydwalter jeffreydwalter commented Mar 27, 2018

@andybons yes, the code works to find the user you are running as. It does not find other users.

I see in your comment, you said, "both as root and as my normal username", but in the output you pasted, I don't see where you ran it as root.

@kazzmir
Copy link

@kazzmir kazzmir commented Feb 19, 2019

I ran into this issue recently using go 1.11.2 when trying to build a darwin binary from linux. The macos host is 10.14.

Given this code

package main

import "fmt"
import "os/user"

func main(){
    me, err := user.Lookup("jon")
    if err != nil {
        fmt.Printf("Could not lookup user 'jon': %v\n", err)
    } else {
        fmt.Printf("Found user 'jon': %+v\n", me)
    }
}

Building this on linux with this command:

# Linux host
$ GOARCH=amd64 GOOS=darwin CGO_ENABLED=0 go build x.go

Or leaving CGO_ENABLED=0 out builds the same way, results in a 64-bit mach-o program that can be executed on macos and produces different behavior depending on whether it is run as root or not.

# Macos host
$ ./x
Found user 'jon': &{Uid:503 Gid:20 Username:jon Name: HomeDir:/Users/jon}
$ sudo ./x
Could not lookup user 'jon': user: unknown user jon

Interestingly (and this is somewhat unrelated to the current issue), if I try to use CGO_ENABLED=1 on my linux host while building a darwin binary then the resulting binary cannot be executed on the macos host due to missing libraries (libc.so.6 in particular).

# Linux host
$ GOARCH=amd64 GOOS=darwin CGO_ENABLED=1 go build x.go
../../Downloads/go1.11.2/src/os/user/getgrouplist_darwin.go: In function ‘mygetgrouplist’:
../../Downloads/go1.11.2/src/os/user/getgrouplist_darwin.go:16:11: warning: implicit declaration of function ‘getgrouplist’; did you mean ‘mygetgrouplist’? [-Wimplicit-function-declaration]
  int rv = getgrouplist(user, (int) group, buf, ngroups);
           ^~~~~~~~~~~~
           mygetgrouplist

(Also note that weird warning as well, but seems harmless I guess)

# Macos host
$ ./x
dyld: Library not loaded: libc.so.6
  Referenced from: /Users/jon/tmp/./x
  Reason: image not found
Abort trap: 6
$ otool -L x
./x:
	/usr/lib/libSystem.B.dylib (compatibility version 0.0.0, current version 0.0.0)
	libc.so.6 (compatibility version 0.0.0, current version 0.0.0)
	libpthread.so.0 (compatibility version 0.0.0, current version 0.0.0)

The mach-o binary produced with CGO_ENABLED=0 has just libSystem.B.dylib as a dependency, rather than libc and libpthread.

For me what this all means is that I must produce the darwin binary on the macos host itself, since CGO_ENABLED=1 is required to get the user.Lookup function to work properly. As others have noted, when CGO_ENABLED=0 is used when building on macos then user.Lookup fails while the program is running under root.

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
9 participants
You can’t perform that action at this time.