...

Source file src/os/user/cgo_lookup_unix.go

Documentation: os/user

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

View as plain text