...

Source file src/os/user/cgo_lookup_unix.go

     1	// Copyright 2011 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	// +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris
     6	// +build cgo,!osusergo
     7	
     8	package user
     9	
    10	import (
    11		"fmt"
    12		"strconv"
    13		"strings"
    14		"syscall"
    15		"unsafe"
    16	)
    17	
    18	/*
    19	#cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS
    20	#include <unistd.h>
    21	#include <sys/types.h>
    22	#include <pwd.h>
    23	#include <grp.h>
    24	#include <stdlib.h>
    25	
    26	static int mygetpwuid_r(int uid, struct passwd *pwd,
    27		char *buf, size_t buflen, struct passwd **result) {
    28		return getpwuid_r(uid, pwd, buf, buflen, result);
    29	}
    30	
    31	static int mygetpwnam_r(const char *name, struct passwd *pwd,
    32		char *buf, size_t buflen, struct passwd **result) {
    33		return getpwnam_r(name, pwd, buf, buflen, result);
    34	}
    35	
    36	static int mygetgrgid_r(int gid, struct group *grp,
    37		char *buf, size_t buflen, struct group **result) {
    38	 return getgrgid_r(gid, grp, buf, buflen, result);
    39	}
    40	
    41	static int mygetgrnam_r(const char *name, struct group *grp,
    42		char *buf, size_t buflen, struct group **result) {
    43	 return getgrnam_r(name, grp, buf, buflen, result);
    44	}
    45	*/
    46	import "C"
    47	
    48	func current() (*User, error) {
    49		return lookupUnixUid(syscall.Getuid())
    50	}
    51	
    52	func lookupUser(username string) (*User, error) {
    53		var pwd C.struct_passwd
    54		var result *C.struct_passwd
    55		nameC := make([]byte, len(username)+1)
    56		copy(nameC, username)
    57	
    58		buf := alloc(userBuffer)
    59		defer buf.free()
    60	
    61		err := retryWithBuffer(buf, func() syscall.Errno {
    62			// mygetpwnam_r is a wrapper around getpwnam_r to avoid
    63			// passing a size_t to getpwnam_r, because for unknown
    64			// reasons passing a size_t to getpwnam_r doesn't work on
    65			// Solaris.
    66			return syscall.Errno(C.mygetpwnam_r((*C.char)(unsafe.Pointer(&nameC[0])),
    67				&pwd,
    68				(*C.char)(buf.ptr),
    69				C.size_t(buf.size),
    70				&result))
    71		})
    72		if err != nil {
    73			return nil, fmt.Errorf("user: lookup username %s: %v", username, err)
    74		}
    75		if result == nil {
    76			return nil, UnknownUserError(username)
    77		}
    78		return buildUser(&pwd), err
    79	}
    80	
    81	func lookupUserId(uid string) (*User, error) {
    82		i, e := strconv.Atoi(uid)
    83		if e != nil {
    84			return nil, e
    85		}
    86		return lookupUnixUid(i)
    87	}
    88	
    89	func lookupUnixUid(uid int) (*User, error) {
    90		var pwd C.struct_passwd
    91		var result *C.struct_passwd
    92	
    93		buf := alloc(userBuffer)
    94		defer buf.free()
    95	
    96		err := retryWithBuffer(buf, func() syscall.Errno {
    97			// mygetpwuid_r is a wrapper around getpwuid_r to avoid using uid_t
    98			// because C.uid_t(uid) for unknown reasons doesn't work on linux.
    99			return syscall.Errno(C.mygetpwuid_r(C.int(uid),
   100				&pwd,
   101				(*C.char)(buf.ptr),
   102				C.size_t(buf.size),
   103				&result))
   104		})
   105		if err != nil {
   106			return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err)
   107		}
   108		if result == nil {
   109			return nil, UnknownUserIdError(uid)
   110		}
   111		return buildUser(&pwd), nil
   112	}
   113	
   114	func buildUser(pwd *C.struct_passwd) *User {
   115		u := &User{
   116			Uid:      strconv.FormatUint(uint64(pwd.pw_uid), 10),
   117			Gid:      strconv.FormatUint(uint64(pwd.pw_gid), 10),
   118			Username: C.GoString(pwd.pw_name),
   119			Name:     C.GoString(pwd.pw_gecos),
   120			HomeDir:  C.GoString(pwd.pw_dir),
   121		}
   122		// The pw_gecos field isn't quite standardized. Some docs
   123		// say: "It is expected to be a comma separated list of
   124		// personal data where the first item is the full name of the
   125		// user."
   126		if i := strings.Index(u.Name, ","); i >= 0 {
   127			u.Name = u.Name[:i]
   128		}
   129		return u
   130	}
   131	
   132	func currentGroup() (*Group, error) {
   133		return lookupUnixGid(syscall.Getgid())
   134	}
   135	
   136	func lookupGroup(groupname string) (*Group, error) {
   137		var grp C.struct_group
   138		var result *C.struct_group
   139	
   140		buf := alloc(groupBuffer)
   141		defer buf.free()
   142		cname := make([]byte, len(groupname)+1)
   143		copy(cname, groupname)
   144	
   145		err := retryWithBuffer(buf, func() syscall.Errno {
   146			return syscall.Errno(C.mygetgrnam_r((*C.char)(unsafe.Pointer(&cname[0])),
   147				&grp,
   148				(*C.char)(buf.ptr),
   149				C.size_t(buf.size),
   150				&result))
   151		})
   152		if err != nil {
   153			return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err)
   154		}
   155		if result == nil {
   156			return nil, UnknownGroupError(groupname)
   157		}
   158		return buildGroup(&grp), nil
   159	}
   160	
   161	func lookupGroupId(gid string) (*Group, error) {
   162		i, e := strconv.Atoi(gid)
   163		if e != nil {
   164			return nil, e
   165		}
   166		return lookupUnixGid(i)
   167	}
   168	
   169	func lookupUnixGid(gid int) (*Group, error) {
   170		var grp C.struct_group
   171		var result *C.struct_group
   172	
   173		buf := alloc(groupBuffer)
   174		defer buf.free()
   175	
   176		err := retryWithBuffer(buf, func() syscall.Errno {
   177			// mygetgrgid_r is a wrapper around getgrgid_r to avoid using gid_t
   178			// because C.gid_t(gid) for unknown reasons doesn't work on linux.
   179			return syscall.Errno(C.mygetgrgid_r(C.int(gid),
   180				&grp,
   181				(*C.char)(buf.ptr),
   182				C.size_t(buf.size),
   183				&result))
   184		})
   185		if err != nil {
   186			return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err)
   187		}
   188		if result == nil {
   189			return nil, UnknownGroupIdError(strconv.Itoa(gid))
   190		}
   191		return buildGroup(&grp), nil
   192	}
   193	
   194	func buildGroup(grp *C.struct_group) *Group {
   195		g := &Group{
   196			Gid:  strconv.Itoa(int(grp.gr_gid)),
   197			Name: C.GoString(grp.gr_name),
   198		}
   199		return g
   200	}
   201	
   202	type bufferKind C.int
   203	
   204	const (
   205		userBuffer  = bufferKind(C._SC_GETPW_R_SIZE_MAX)
   206		groupBuffer = bufferKind(C._SC_GETGR_R_SIZE_MAX)
   207	)
   208	
   209	func (k bufferKind) initialSize() C.size_t {
   210		sz := C.sysconf(C.int(k))
   211		if sz == -1 {
   212			// DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX.
   213			// Additionally, not all Linux systems have it, either. For
   214			// example, the musl libc returns -1.
   215			return 1024
   216		}
   217		if !isSizeReasonable(int64(sz)) {
   218			// Truncate.  If this truly isn't enough, retryWithBuffer will error on the first run.
   219			return maxBufferSize
   220		}
   221		return C.size_t(sz)
   222	}
   223	
   224	type memBuffer struct {
   225		ptr  unsafe.Pointer
   226		size C.size_t
   227	}
   228	
   229	func alloc(kind bufferKind) *memBuffer {
   230		sz := kind.initialSize()
   231		return &memBuffer{
   232			ptr:  C.malloc(sz),
   233			size: sz,
   234		}
   235	}
   236	
   237	func (mb *memBuffer) resize(newSize C.size_t) {
   238		mb.ptr = C.realloc(mb.ptr, newSize)
   239		mb.size = newSize
   240	}
   241	
   242	func (mb *memBuffer) free() {
   243		C.free(mb.ptr)
   244	}
   245	
   246	// retryWithBuffer repeatedly calls f(), increasing the size of the
   247	// buffer each time, until f succeeds, fails with a non-ERANGE error,
   248	// or the buffer exceeds a reasonable limit.
   249	func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error {
   250		for {
   251			errno := f()
   252			if errno == 0 {
   253				return nil
   254			} else if errno != syscall.ERANGE {
   255				return errno
   256			}
   257			newSize := buf.size * 2
   258			if !isSizeReasonable(int64(newSize)) {
   259				return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize)
   260			}
   261			buf.resize(newSize)
   262		}
   263	}
   264	
   265	const maxBufferSize = 1 << 20
   266	
   267	func isSizeReasonable(sz int64) bool {
   268		return sz > 0 && sz <= maxBufferSize
   269	}
   270	
   271	// Because we can't use cgo in tests:
   272	func structPasswdForNegativeTest() C.struct_passwd {
   273		sp := C.struct_passwd{}
   274		sp.pw_uid = 1<<32 - 2
   275		sp.pw_gid = 1<<32 - 3
   276		return sp
   277	}
   278	

View as plain text