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  // +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