diff options
author | hiddenpath <hiddenpath@yandex-team.com> | 2024-04-02 23:50:23 +0300 |
---|---|---|
committer | hiddenpath <hiddenpath@yandex-team.com> | 2024-04-03 00:02:31 +0300 |
commit | 8923c6d2c438e0aeed2e06b8b0275e1864eeee33 (patch) | |
tree | 6b5e476699fc0be5091cb650654ef5f602c8afff /contrib/go/_std_1.22/src/os/user | |
parent | d18afd09df2a08cd023012593b46109b77713a6c (diff) | |
download | ydb-8923c6d2c438e0aeed2e06b8b0275e1864eeee33.tar.gz |
Update golang to 1.22.1
2967d19c907adf59101a1f47b4208bd0b04a6186
Diffstat (limited to 'contrib/go/_std_1.22/src/os/user')
-rw-r--r-- | contrib/go/_std_1.22/src/os/user/cgo_listgroups_unix.go | 57 | ||||
-rw-r--r-- | contrib/go/_std_1.22/src/os/user/cgo_lookup_cgo.go | 112 | ||||
-rw-r--r-- | contrib/go/_std_1.22/src/os/user/cgo_lookup_syscall.go | 65 | ||||
-rw-r--r-- | contrib/go/_std_1.22/src/os/user/cgo_lookup_unix.go | 200 | ||||
-rw-r--r-- | contrib/go/_std_1.22/src/os/user/getgrouplist_syscall.go | 19 | ||||
-rw-r--r-- | contrib/go/_std_1.22/src/os/user/getgrouplist_unix.go | 22 | ||||
-rw-r--r-- | contrib/go/_std_1.22/src/os/user/lookup.go | 70 | ||||
-rw-r--r-- | contrib/go/_std_1.22/src/os/user/lookup_windows.go | 392 | ||||
-rw-r--r-- | contrib/go/_std_1.22/src/os/user/user.go | 95 | ||||
-rw-r--r-- | contrib/go/_std_1.22/src/os/user/ya.make | 40 |
10 files changed, 1072 insertions, 0 deletions
diff --git a/contrib/go/_std_1.22/src/os/user/cgo_listgroups_unix.go b/contrib/go/_std_1.22/src/os/user/cgo_listgroups_unix.go new file mode 100644 index 0000000000..59636954b2 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/cgo_listgroups_unix.go @@ -0,0 +1,57 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (cgo || darwin) && !osusergo && (darwin || dragonfly || freebsd || (linux && !android) || netbsd || openbsd || (solaris && !illumos)) + +package user + +import ( + "fmt" + "strconv" + "unsafe" +) + +const maxGroups = 2048 + +func listGroups(u *User) ([]string, error) { + ug, err := strconv.Atoi(u.Gid) + if err != nil { + return nil, fmt.Errorf("user: list groups for %s: invalid gid %q", u.Username, u.Gid) + } + userGID := _C_gid_t(ug) + nameC := make([]byte, len(u.Username)+1) + copy(nameC, u.Username) + + n := _C_int(256) + gidsC := make([]_C_gid_t, n) + rv := getGroupList((*_C_char)(unsafe.Pointer(&nameC[0])), userGID, &gidsC[0], &n) + if rv == -1 { + // Mac is the only Unix that does not set n properly when rv == -1, so + // we need to use different logic for Mac vs. the other OS's. + if err := groupRetry(u.Username, nameC, userGID, &gidsC, &n); err != nil { + return nil, err + } + } + gidsC = gidsC[:n] + gids := make([]string, 0, n) + for _, g := range gidsC[:n] { + gids = append(gids, strconv.Itoa(int(g))) + } + return gids, nil +} + +// groupRetry retries getGroupList with much larger size for n. The result is +// stored in gids. +func groupRetry(username string, name []byte, userGID _C_gid_t, gids *[]_C_gid_t, n *_C_int) error { + // More than initial buffer, but now n contains the correct size. + if *n > maxGroups { + return fmt.Errorf("user: %q is a member of more than %d groups", username, maxGroups) + } + *gids = make([]_C_gid_t, *n) + rv := getGroupList((*_C_char)(unsafe.Pointer(&name[0])), userGID, &(*gids)[0], n) + if rv == -1 { + return fmt.Errorf("user: list groups for %s failed", username) + } + return nil +} diff --git a/contrib/go/_std_1.22/src/os/user/cgo_lookup_cgo.go b/contrib/go/_std_1.22/src/os/user/cgo_lookup_cgo.go new file mode 100644 index 0000000000..4f78dcad23 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/cgo_lookup_cgo.go @@ -0,0 +1,112 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build cgo && !osusergo && unix && !android && !darwin + +package user + +import ( + "syscall" +) + +/* +#cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS +#cgo CFLAGS: -fno-stack-protector +#include <unistd.h> +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> +#include <stdlib.h> +#include <string.h> + +static struct passwd mygetpwuid_r(int uid, char *buf, size_t buflen, int *found, int *perr) { + struct passwd pwd; + struct passwd *result; + memset (&pwd, 0, sizeof(pwd)); + *perr = getpwuid_r(uid, &pwd, buf, buflen, &result); + *found = result != NULL; + return pwd; +} + +static struct passwd mygetpwnam_r(const char *name, char *buf, size_t buflen, int *found, int *perr) { + struct passwd pwd; + struct passwd *result; + memset(&pwd, 0, sizeof(pwd)); + *perr = getpwnam_r(name, &pwd, buf, buflen, &result); + *found = result != NULL; + return pwd; +} + +static struct group mygetgrgid_r(int gid, char *buf, size_t buflen, int *found, int *perr) { + struct group grp; + struct group *result; + memset(&grp, 0, sizeof(grp)); + *perr = getgrgid_r(gid, &grp, buf, buflen, &result); + *found = result != NULL; + return grp; +} + +static struct group mygetgrnam_r(const char *name, char *buf, size_t buflen, int *found, int *perr) { + struct group grp; + struct group *result; + memset(&grp, 0, sizeof(grp)); + *perr = getgrnam_r(name, &grp, buf, buflen, &result); + *found = result != NULL; + return grp; +} +*/ +import "C" + +type _C_char = C.char +type _C_int = C.int +type _C_gid_t = C.gid_t +type _C_uid_t = C.uid_t +type _C_size_t = C.size_t +type _C_struct_group = C.struct_group +type _C_struct_passwd = C.struct_passwd +type _C_long = C.long + +func _C_pw_uid(p *_C_struct_passwd) _C_uid_t { return p.pw_uid } +func _C_pw_uidp(p *_C_struct_passwd) *_C_uid_t { return &p.pw_uid } +func _C_pw_gid(p *_C_struct_passwd) _C_gid_t { return p.pw_gid } +func _C_pw_gidp(p *_C_struct_passwd) *_C_gid_t { return &p.pw_gid } +func _C_pw_name(p *_C_struct_passwd) *_C_char { return p.pw_name } +func _C_pw_gecos(p *_C_struct_passwd) *_C_char { return p.pw_gecos } +func _C_pw_dir(p *_C_struct_passwd) *_C_char { return p.pw_dir } + +func _C_gr_gid(g *_C_struct_group) _C_gid_t { return g.gr_gid } +func _C_gr_name(g *_C_struct_group) *_C_char { return g.gr_name } + +func _C_GoString(p *_C_char) string { return C.GoString(p) } + +func _C_getpwnam_r(name *_C_char, buf *_C_char, size _C_size_t) (pwd _C_struct_passwd, found bool, errno syscall.Errno) { + var f, e _C_int + pwd = C.mygetpwnam_r(name, buf, size, &f, &e) + return pwd, f != 0, syscall.Errno(e) +} + +func _C_getpwuid_r(uid _C_uid_t, buf *_C_char, size _C_size_t) (pwd _C_struct_passwd, found bool, errno syscall.Errno) { + var f, e _C_int + pwd = C.mygetpwuid_r(_C_int(uid), buf, size, &f, &e) + return pwd, f != 0, syscall.Errno(e) +} + +func _C_getgrnam_r(name *_C_char, buf *_C_char, size _C_size_t) (grp _C_struct_group, found bool, errno syscall.Errno) { + var f, e _C_int + grp = C.mygetgrnam_r(name, buf, size, &f, &e) + return grp, f != 0, syscall.Errno(e) +} + +func _C_getgrgid_r(gid _C_gid_t, buf *_C_char, size _C_size_t) (grp _C_struct_group, found bool, errno syscall.Errno) { + var f, e _C_int + grp = C.mygetgrgid_r(_C_int(gid), buf, size, &f, &e) + return grp, f != 0, syscall.Errno(e) +} + +const ( + _C__SC_GETPW_R_SIZE_MAX = C._SC_GETPW_R_SIZE_MAX + _C__SC_GETGR_R_SIZE_MAX = C._SC_GETGR_R_SIZE_MAX +) + +func _C_sysconf(key _C_int) _C_long { return C.sysconf(key) } diff --git a/contrib/go/_std_1.22/src/os/user/cgo_lookup_syscall.go b/contrib/go/_std_1.22/src/os/user/cgo_lookup_syscall.go new file mode 100644 index 0000000000..321df652be --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/cgo_lookup_syscall.go @@ -0,0 +1,65 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !osusergo && darwin + +package user + +import ( + "internal/syscall/unix" + "syscall" +) + +type _C_char = byte +type _C_int = int32 +type _C_gid_t = uint32 +type _C_uid_t = uint32 +type _C_size_t = uintptr +type _C_struct_group = unix.Group +type _C_struct_passwd = unix.Passwd +type _C_long = int64 + +func _C_pw_uid(p *_C_struct_passwd) _C_uid_t { return p.Uid } +func _C_pw_uidp(p *_C_struct_passwd) *_C_uid_t { return &p.Uid } +func _C_pw_gid(p *_C_struct_passwd) _C_gid_t { return p.Gid } +func _C_pw_gidp(p *_C_struct_passwd) *_C_gid_t { return &p.Gid } +func _C_pw_name(p *_C_struct_passwd) *_C_char { return p.Name } +func _C_pw_gecos(p *_C_struct_passwd) *_C_char { return p.Gecos } +func _C_pw_dir(p *_C_struct_passwd) *_C_char { return p.Dir } + +func _C_gr_gid(g *_C_struct_group) _C_gid_t { return g.Gid } +func _C_gr_name(g *_C_struct_group) *_C_char { return g.Name } + +func _C_GoString(p *_C_char) string { return unix.GoString(p) } + +func _C_getpwnam_r(name *_C_char, buf *_C_char, size _C_size_t) (pwd _C_struct_passwd, found bool, errno syscall.Errno) { + var result *_C_struct_passwd + errno = unix.Getpwnam(name, &pwd, buf, size, &result) + return pwd, result != nil, errno +} + +func _C_getpwuid_r(uid _C_uid_t, buf *_C_char, size _C_size_t) (pwd _C_struct_passwd, found bool, errno syscall.Errno) { + var result *_C_struct_passwd + errno = unix.Getpwuid(uid, &pwd, buf, size, &result) + return pwd, result != nil, errno +} + +func _C_getgrnam_r(name *_C_char, buf *_C_char, size _C_size_t) (grp _C_struct_group, found bool, errno syscall.Errno) { + var result *_C_struct_group + errno = unix.Getgrnam(name, &grp, buf, size, &result) + return grp, result != nil, errno +} + +func _C_getgrgid_r(gid _C_gid_t, buf *_C_char, size _C_size_t) (grp _C_struct_group, found bool, errno syscall.Errno) { + var result *_C_struct_group + errno = unix.Getgrgid(gid, &grp, buf, size, &result) + return grp, result != nil, errno +} + +const ( + _C__SC_GETPW_R_SIZE_MAX = unix.SC_GETPW_R_SIZE_MAX + _C__SC_GETGR_R_SIZE_MAX = unix.SC_GETGR_R_SIZE_MAX +) + +func _C_sysconf(key _C_int) _C_long { return unix.Sysconf(key) } diff --git a/contrib/go/_std_1.22/src/os/user/cgo_lookup_unix.go b/contrib/go/_std_1.22/src/os/user/cgo_lookup_unix.go new file mode 100644 index 0000000000..402429ba4a --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/cgo_lookup_unix.go @@ -0,0 +1,200 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (cgo || darwin) && !osusergo && unix && !android + +package user + +import ( + "fmt" + "runtime" + "strconv" + "strings" + "syscall" + "unsafe" +) + +func current() (*User, error) { + return lookupUnixUid(syscall.Getuid()) +} + +func lookupUser(username string) (*User, error) { + var pwd _C_struct_passwd + var found bool + nameC := make([]byte, len(username)+1) + copy(nameC, username) + + err := retryWithBuffer(userBuffer, func(buf []byte) syscall.Errno { + var errno syscall.Errno + pwd, found, errno = _C_getpwnam_r((*_C_char)(unsafe.Pointer(&nameC[0])), + (*_C_char)(unsafe.Pointer(&buf[0])), _C_size_t(len(buf))) + return errno + }) + if err != nil { + return nil, fmt.Errorf("user: lookup username %s: %v", username, err) + } + if !found { + return nil, UnknownUserError(username) + } + return buildUser(&pwd), err +} + +func lookupUserId(uid string) (*User, error) { + i, e := strconv.Atoi(uid) + if e != nil { + return nil, e + } + return lookupUnixUid(i) +} + +func lookupUnixUid(uid int) (*User, error) { + var pwd _C_struct_passwd + var found bool + + err := retryWithBuffer(userBuffer, func(buf []byte) syscall.Errno { + var errno syscall.Errno + pwd, found, errno = _C_getpwuid_r(_C_uid_t(uid), + (*_C_char)(unsafe.Pointer(&buf[0])), _C_size_t(len(buf))) + return errno + }) + if err != nil { + return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err) + } + if !found { + return nil, UnknownUserIdError(uid) + } + return buildUser(&pwd), nil +} + +func buildUser(pwd *_C_struct_passwd) *User { + u := &User{ + Uid: strconv.FormatUint(uint64(_C_pw_uid(pwd)), 10), + Gid: strconv.FormatUint(uint64(_C_pw_gid(pwd)), 10), + Username: _C_GoString(_C_pw_name(pwd)), + Name: _C_GoString(_C_pw_gecos(pwd)), + HomeDir: _C_GoString(_C_pw_dir(pwd)), + } + // The pw_gecos field isn't quite standardized. Some docs + // say: "It is expected to be a comma separated list of + // personal data where the first item is the full name of the + // user." + u.Name, _, _ = strings.Cut(u.Name, ",") + return u +} + +func lookupGroup(groupname string) (*Group, error) { + var grp _C_struct_group + var found bool + + cname := make([]byte, len(groupname)+1) + copy(cname, groupname) + + err := retryWithBuffer(groupBuffer, func(buf []byte) syscall.Errno { + var errno syscall.Errno + grp, found, errno = _C_getgrnam_r((*_C_char)(unsafe.Pointer(&cname[0])), + (*_C_char)(unsafe.Pointer(&buf[0])), _C_size_t(len(buf))) + return errno + }) + if err != nil { + return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err) + } + if !found { + return nil, UnknownGroupError(groupname) + } + return buildGroup(&grp), nil +} + +func lookupGroupId(gid string) (*Group, error) { + i, e := strconv.Atoi(gid) + if e != nil { + return nil, e + } + return lookupUnixGid(i) +} + +func lookupUnixGid(gid int) (*Group, error) { + var grp _C_struct_group + var found bool + + err := retryWithBuffer(groupBuffer, func(buf []byte) syscall.Errno { + var errno syscall.Errno + grp, found, errno = _C_getgrgid_r(_C_gid_t(gid), + (*_C_char)(unsafe.Pointer(&buf[0])), _C_size_t(len(buf))) + return syscall.Errno(errno) + }) + if err != nil { + return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err) + } + if !found { + return nil, UnknownGroupIdError(strconv.Itoa(gid)) + } + return buildGroup(&grp), nil +} + +func buildGroup(grp *_C_struct_group) *Group { + g := &Group{ + Gid: strconv.Itoa(int(_C_gr_gid(grp))), + Name: _C_GoString(_C_gr_name(grp)), + } + return g +} + +type bufferKind _C_int + +var ( + userBuffer = bufferKind(_C__SC_GETPW_R_SIZE_MAX) + groupBuffer = bufferKind(_C__SC_GETGR_R_SIZE_MAX) +) + +func (k bufferKind) initialSize() _C_size_t { + sz := _C_sysconf(_C_int(k)) + if sz == -1 { + // DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX. + // Additionally, not all Linux systems have it, either. For + // example, the musl libc returns -1. + return 1024 + } + if !isSizeReasonable(int64(sz)) { + // Truncate. If this truly isn't enough, retryWithBuffer will error on the first run. + return maxBufferSize + } + return _C_size_t(sz) +} + +// retryWithBuffer repeatedly calls f(), increasing the size of the +// buffer each time, until f succeeds, fails with a non-ERANGE error, +// or the buffer exceeds a reasonable limit. +func retryWithBuffer(kind bufferKind, f func([]byte) syscall.Errno) error { + buf := make([]byte, kind.initialSize()) + for { + errno := f(buf) + if errno == 0 { + return nil + } else if runtime.GOOS == "aix" && errno+1 == 0 { + // On AIX getpwuid_r appears to return -1, + // not ERANGE, on buffer overflow. + } else if errno != syscall.ERANGE { + return errno + } + newSize := len(buf) * 2 + if !isSizeReasonable(int64(newSize)) { + return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize) + } + buf = make([]byte, newSize) + } +} + +const maxBufferSize = 1 << 20 + +func isSizeReasonable(sz int64) bool { + return sz > 0 && sz <= maxBufferSize +} + +// Because we can't use cgo in tests: +func structPasswdForNegativeTest() _C_struct_passwd { + sp := _C_struct_passwd{} + *_C_pw_uidp(&sp) = 1<<32 - 2 + *_C_pw_gidp(&sp) = 1<<32 - 3 + return sp +} diff --git a/contrib/go/_std_1.22/src/os/user/getgrouplist_syscall.go b/contrib/go/_std_1.22/src/os/user/getgrouplist_syscall.go new file mode 100644 index 0000000000..41b64fca93 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/getgrouplist_syscall.go @@ -0,0 +1,19 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !osusergo && darwin + +package user + +import ( + "internal/syscall/unix" +) + +func getGroupList(name *_C_char, userGID _C_gid_t, gids *_C_gid_t, n *_C_int) _C_int { + err := unix.Getgrouplist(name, userGID, gids, n) + if err != nil { + return -1 + } + return 0 +} diff --git a/contrib/go/_std_1.22/src/os/user/getgrouplist_unix.go b/contrib/go/_std_1.22/src/os/user/getgrouplist_unix.go new file mode 100644 index 0000000000..fb482d35ba --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/getgrouplist_unix.go @@ -0,0 +1,22 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build cgo && !osusergo && (dragonfly || freebsd || (!android && linux) || netbsd || openbsd || (solaris && !illumos)) + +package user + +/* +#include <unistd.h> +#include <sys/types.h> +#include <grp.h> + +static int mygetgrouplist(const char* user, gid_t group, gid_t* groups, int* ngroups) { + return getgrouplist(user, group, groups, ngroups); +} +*/ +import "C" + +func getGroupList(name *_C_char, userGID _C_gid_t, gids *_C_gid_t, n *_C_int) _C_int { + return C.mygetgrouplist(name, userGID, gids, n) +} diff --git a/contrib/go/_std_1.22/src/os/user/lookup.go b/contrib/go/_std_1.22/src/os/user/lookup.go new file mode 100644 index 0000000000..ed33d0c7cd --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/lookup.go @@ -0,0 +1,70 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package user + +import "sync" + +const ( + userFile = "/etc/passwd" + groupFile = "/etc/group" +) + +var colon = []byte{':'} + +// Current returns the current user. +// +// The first call will cache the current user information. +// Subsequent calls will return the cached value and will not reflect +// changes to the current user. +func Current() (*User, error) { + cache.Do(func() { cache.u, cache.err = current() }) + if cache.err != nil { + return nil, cache.err + } + u := *cache.u // copy + return &u, nil +} + +// cache of the current user +var cache struct { + sync.Once + u *User + err error +} + +// Lookup looks up a user by username. If the user cannot be found, the +// returned error is of type UnknownUserError. +func Lookup(username string) (*User, error) { + if u, err := Current(); err == nil && u.Username == username { + return u, err + } + return lookupUser(username) +} + +// LookupId looks up a user by userid. If the user cannot be found, the +// returned error is of type UnknownUserIdError. +func LookupId(uid string) (*User, error) { + if u, err := Current(); err == nil && u.Uid == uid { + return u, err + } + return lookupUserId(uid) +} + +// LookupGroup looks up a group by name. If the group cannot be found, the +// returned error is of type UnknownGroupError. +func LookupGroup(name string) (*Group, error) { + return lookupGroup(name) +} + +// LookupGroupId looks up a group by groupid. If the group cannot be found, the +// returned error is of type UnknownGroupIdError. +func LookupGroupId(gid string) (*Group, error) { + return lookupGroupId(gid) +} + +// GroupIds returns the list of group IDs that the user is a member of. +func (u *User) GroupIds() ([]string, error) { + return listGroups(u) +} diff --git a/contrib/go/_std_1.22/src/os/user/lookup_windows.go b/contrib/go/_std_1.22/src/os/user/lookup_windows.go new file mode 100644 index 0000000000..a48fc89720 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/lookup_windows.go @@ -0,0 +1,392 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package user + +import ( + "fmt" + "internal/syscall/windows" + "internal/syscall/windows/registry" + "syscall" + "unsafe" +) + +func isDomainJoined() (bool, error) { + var domain *uint16 + var status uint32 + err := syscall.NetGetJoinInformation(nil, &domain, &status) + if err != nil { + return false, err + } + syscall.NetApiBufferFree((*byte)(unsafe.Pointer(domain))) + return status == syscall.NetSetupDomainName, nil +} + +func lookupFullNameDomain(domainAndUser string) (string, error) { + return syscall.TranslateAccountName(domainAndUser, + syscall.NameSamCompatible, syscall.NameDisplay, 50) +} + +func lookupFullNameServer(servername, username string) (string, error) { + s, e := syscall.UTF16PtrFromString(servername) + if e != nil { + return "", e + } + u, e := syscall.UTF16PtrFromString(username) + if e != nil { + return "", e + } + var p *byte + e = syscall.NetUserGetInfo(s, u, 10, &p) + if e != nil { + return "", e + } + defer syscall.NetApiBufferFree(p) + i := (*syscall.UserInfo10)(unsafe.Pointer(p)) + return windows.UTF16PtrToString(i.FullName), nil +} + +func lookupFullName(domain, username, domainAndUser string) (string, error) { + joined, err := isDomainJoined() + if err == nil && joined { + name, err := lookupFullNameDomain(domainAndUser) + if err == nil { + return name, nil + } + } + name, err := lookupFullNameServer(domain, username) + if err == nil { + return name, nil + } + // domain worked neither as a domain nor as a server + // could be domain server unavailable + // pretend username is fullname + return username, nil +} + +// getProfilesDirectory retrieves the path to the root directory +// where user profiles are stored. +func getProfilesDirectory() (string, error) { + n := uint32(100) + for { + b := make([]uint16, n) + e := windows.GetProfilesDirectory(&b[0], &n) + if e == nil { + return syscall.UTF16ToString(b), nil + } + if e != syscall.ERROR_INSUFFICIENT_BUFFER { + return "", e + } + if n <= uint32(len(b)) { + return "", e + } + } +} + +// lookupUsernameAndDomain obtains the username and domain for usid. +func lookupUsernameAndDomain(usid *syscall.SID) (username, domain string, e error) { + username, domain, t, e := usid.LookupAccount("") + if e != nil { + return "", "", e + } + if t != syscall.SidTypeUser { + return "", "", fmt.Errorf("user: should be user account type, not %d", t) + } + return username, domain, nil +} + +// findHomeDirInRegistry finds the user home path based on the uid. +func findHomeDirInRegistry(uid string) (dir string, e error) { + k, e := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\`+uid, registry.QUERY_VALUE) + if e != nil { + return "", e + } + defer k.Close() + dir, _, e = k.GetStringValue("ProfileImagePath") + if e != nil { + return "", e + } + return dir, nil +} + +// lookupGroupName accepts the name of a group and retrieves the group SID. +func lookupGroupName(groupname string) (string, error) { + sid, _, t, e := syscall.LookupSID("", groupname) + if e != nil { + return "", e + } + // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/7b2aeb27-92fc-41f6-8437-deb65d950921#gt_0387e636-5654-4910-9519-1f8326cf5ec0 + // SidTypeAlias should also be treated as a group type next to SidTypeGroup + // and SidTypeWellKnownGroup: + // "alias object -> resource group: A group object..." + // + // Tests show that "Administrators" can be considered of type SidTypeAlias. + if t != syscall.SidTypeGroup && t != syscall.SidTypeWellKnownGroup && t != syscall.SidTypeAlias { + return "", fmt.Errorf("lookupGroupName: should be group account type, not %d", t) + } + return sid.String() +} + +// listGroupsForUsernameAndDomain accepts username and domain and retrieves +// a SID list of the local groups where this user is a member. +func listGroupsForUsernameAndDomain(username, domain string) ([]string, error) { + // Check if both the domain name and user should be used. + var query string + joined, err := isDomainJoined() + if err == nil && joined && len(domain) != 0 { + query = domain + `\` + username + } else { + query = username + } + q, err := syscall.UTF16PtrFromString(query) + if err != nil { + return nil, err + } + var p0 *byte + var entriesRead, totalEntries uint32 + // https://learn.microsoft.com/en-us/windows/win32/api/lmaccess/nf-lmaccess-netusergetlocalgroups + // NetUserGetLocalGroups() would return a list of LocalGroupUserInfo0 + // elements which hold the names of local groups where the user participates. + // The list does not follow any sorting order. + // + // If no groups can be found for this user, NetUserGetLocalGroups() should + // always return the SID of a single group called "None", which + // also happens to be the primary group for the local user. + err = windows.NetUserGetLocalGroups(nil, q, 0, windows.LG_INCLUDE_INDIRECT, &p0, windows.MAX_PREFERRED_LENGTH, &entriesRead, &totalEntries) + if err != nil { + return nil, err + } + defer syscall.NetApiBufferFree(p0) + if entriesRead == 0 { + return nil, fmt.Errorf("listGroupsForUsernameAndDomain: NetUserGetLocalGroups() returned an empty list for domain: %s, username: %s", domain, username) + } + entries := (*[1024]windows.LocalGroupUserInfo0)(unsafe.Pointer(p0))[:entriesRead:entriesRead] + var sids []string + for _, entry := range entries { + if entry.Name == nil { + continue + } + sid, err := lookupGroupName(windows.UTF16PtrToString(entry.Name)) + if err != nil { + return nil, err + } + sids = append(sids, sid) + } + return sids, nil +} + +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, + } + return u, nil +} + +var ( + // unused variables (in this implementation) + // modified during test to exercise code paths in the cgo implementation. + userBuffer = 0 + groupBuffer = 0 +) + +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() + if e != nil { + return nil, e + } + username, domain, e := lookupUsernameAndDomain(u.User.Sid) + if e != nil { + return nil, e + } + return newUser(uid, gid, dir, username, domain) +} + +// lookupUserPrimaryGroup obtains the primary group SID for a user using this method: +// https://support.microsoft.com/en-us/help/297951/how-to-use-the-primarygroupid-attribute-to-find-the-primary-group-for +// The method follows this formula: domainRID + "-" + primaryGroupRID +func lookupUserPrimaryGroup(username, domain string) (string, error) { + // get the domain RID + sid, _, t, e := syscall.LookupSID("", domain) + if e != nil { + return "", e + } + if t != syscall.SidTypeDomain { + return "", fmt.Errorf("lookupUserPrimaryGroup: should be domain account type, not %d", t) + } + domainRID, e := sid.String() + if e != nil { + return "", e + } + // If the user has joined a domain use the RID of the default primary group + // called "Domain Users": + // https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems + // SID: S-1-5-21domain-513 + // + // The correct way to obtain the primary group of a domain user is + // probing the user primaryGroupID attribute in the server Active Directory: + // https://learn.microsoft.com/en-us/windows/win32/adschema/a-primarygroupid + // + // Note that the primary group of domain users should not be modified + // on Windows for performance reasons, even if it's possible to do that. + // The .NET Developer's Guide to Directory Services Programming - Page 409 + // https://books.google.bg/books?id=kGApqjobEfsC&lpg=PA410&ots=p7oo-eOQL7&dq=primary%20group%20RID&hl=bg&pg=PA409#v=onepage&q&f=false + joined, err := isDomainJoined() + if err == nil && joined { + return domainRID + "-513", nil + } + // For non-domain users call NetUserGetInfo() with level 4, which + // in this case would not have any network overhead. + // The primary group should not change from RID 513 here either + // but the group will be called "None" instead: + // https://www.adampalmer.me/iodigitalsec/2013/08/10/windows-null-session-enumeration/ + // "Group 'None' (RID: 513)" + u, e := syscall.UTF16PtrFromString(username) + if e != nil { + return "", e + } + d, e := syscall.UTF16PtrFromString(domain) + if e != nil { + return "", e + } + var p *byte + e = syscall.NetUserGetInfo(d, u, 4, &p) + if e != nil { + return "", e + } + defer syscall.NetApiBufferFree(p) + i := (*windows.UserInfo4)(unsafe.Pointer(p)) + return fmt.Sprintf("%s-%d", domainRID, i.PrimaryGroupID), nil +} + +func newUserFromSid(usid *syscall.SID) (*User, error) { + username, domain, e := lookupUsernameAndDomain(usid) + if e != nil { + return nil, e + } + gid, e := lookupUserPrimaryGroup(username, domain) + if e != nil { + return nil, e + } + uid, e := usid.String() + if e != nil { + return nil, e + } + // If this user has logged in at least once their home path should be stored + // in the registry under the specified SID. References: + // https://social.technet.microsoft.com/wiki/contents/articles/13895.how-to-remove-a-corrupted-user-profile-from-the-registry.aspx + // https://support.asperasoft.com/hc/en-us/articles/216127438-How-to-delete-Windows-user-profiles + // + // The registry is the most reliable way to find the home path as the user + // might have decided to move it outside of the default location, + // (e.g. C:\users). Reference: + // https://answers.microsoft.com/en-us/windows/forum/windows_7-security/how-do-i-set-a-home-directory-outside-cusers-for-a/aed68262-1bf4-4a4d-93dc-7495193a440f + dir, e := findHomeDirInRegistry(uid) + if e != nil { + // If the home path does not exist in the registry, the user might + // have not logged in yet; fall back to using getProfilesDirectory(). + // Find the username based on a SID and append that to the result of + // getProfilesDirectory(). The domain is not relevant here. + dir, e = getProfilesDirectory() + if e != nil { + return nil, e + } + dir += `\` + username + } + return newUser(uid, gid, dir, username, domain) +} + +func lookupUser(username string) (*User, error) { + sid, _, t, e := syscall.LookupSID("", username) + if e != nil { + return nil, e + } + if t != syscall.SidTypeUser { + return nil, fmt.Errorf("user: should be user account type, not %d", t) + } + return newUserFromSid(sid) +} + +func lookupUserId(uid string) (*User, error) { + sid, e := syscall.StringToSid(uid) + if e != nil { + return nil, e + } + return newUserFromSid(sid) +} + +func lookupGroup(groupname string) (*Group, error) { + sid, err := lookupGroupName(groupname) + if err != nil { + return nil, err + } + return &Group{Name: groupname, Gid: sid}, nil +} + +func lookupGroupId(gid string) (*Group, error) { + sid, err := syscall.StringToSid(gid) + if err != nil { + return nil, err + } + groupname, _, t, err := sid.LookupAccount("") + if err != nil { + return nil, err + } + if t != syscall.SidTypeGroup && t != syscall.SidTypeWellKnownGroup && t != syscall.SidTypeAlias { + return nil, fmt.Errorf("lookupGroupId: should be group account type, not %d", t) + } + return &Group{Name: groupname, Gid: gid}, nil +} + +func listGroups(user *User) ([]string, error) { + sid, err := syscall.StringToSid(user.Uid) + if err != nil { + return nil, err + } + username, domain, err := lookupUsernameAndDomain(sid) + if err != nil { + return nil, err + } + sids, err := listGroupsForUsernameAndDomain(username, domain) + if err != nil { + return nil, err + } + // Add the primary group of the user to the list if it is not already there. + // This is done only to comply with the POSIX concept of a primary group. + for _, sid := range sids { + if sid == user.Gid { + return sids, nil + } + } + return append(sids, user.Gid), nil +} diff --git a/contrib/go/_std_1.22/src/os/user/user.go b/contrib/go/_std_1.22/src/os/user/user.go new file mode 100644 index 0000000000..0307d2ad6a --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/user.go @@ -0,0 +1,95 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package user allows user account lookups by name or id. + +For most Unix systems, this package has two internal implementations of +resolving user and group ids to names, and listing supplementary group IDs. +One is written in pure Go and parses /etc/passwd and /etc/group. The other +is cgo-based and relies on the standard C library (libc) routines such as +getpwuid_r, getgrnam_r, and getgrouplist. + +When cgo is available, and the required routines are implemented in libc +for a particular platform, cgo-based (libc-backed) code is used. +This can be overridden by using osusergo build tag, which enforces +the pure Go implementation. +*/ +package user + +import ( + "strconv" +) + +// These may be set to false in init() for a particular platform and/or +// build flags to let the tests know to skip tests of some features. +var ( + userImplemented = true + groupImplemented = true + groupListImplemented = true +) + +// User represents a user account. +type User struct { + // Uid is the user ID. + // On POSIX systems, this is a decimal number representing the uid. + // On Windows, this is a security identifier (SID) in a string format. + // On Plan 9, this is the contents of /dev/user. + Uid string + // Gid is the primary group ID. + // On POSIX systems, this is a decimal number representing the gid. + // On Windows, this is a SID in a string format. + // On Plan 9, this is the contents of /dev/user. + Gid string + // Username is the login name. + Username string + // Name is the user's real or display name. + // It might be blank. + // On POSIX systems, this is the first (or only) entry in the GECOS field + // list. + // On Windows, this is the user's display name. + // On Plan 9, this is the contents of /dev/user. + Name string + // HomeDir is the path to the user's home directory (if they have one). + HomeDir string +} + +// Group represents a grouping of users. +// +// On POSIX systems Gid contains a decimal number representing the group ID. +type Group struct { + Gid string // group ID + Name string // group name +} + +// UnknownUserIdError is returned by LookupId when a user cannot be found. +type UnknownUserIdError int + +func (e UnknownUserIdError) Error() string { + return "user: unknown userid " + strconv.Itoa(int(e)) +} + +// UnknownUserError is returned by Lookup when +// a user cannot be found. +type UnknownUserError string + +func (e UnknownUserError) Error() string { + return "user: unknown user " + string(e) +} + +// UnknownGroupIdError is returned by LookupGroupId when +// a group cannot be found. +type UnknownGroupIdError string + +func (e UnknownGroupIdError) Error() string { + return "group: unknown groupid " + string(e) +} + +// UnknownGroupError is returned by LookupGroup when +// a group cannot be found. +type UnknownGroupError string + +func (e UnknownGroupError) Error() string { + return "group: unknown group " + string(e) +} diff --git a/contrib/go/_std_1.22/src/os/user/ya.make b/contrib/go/_std_1.22/src/os/user/ya.make new file mode 100644 index 0000000000..3bf6d0640d --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/ya.make @@ -0,0 +1,40 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + cgo_listgroups_unix.go + cgo_lookup_syscall.go + cgo_lookup_unix.go + getgrouplist_syscall.go + lookup.go + user.go + ) +ELSEIF (OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED) + SRCS( + cgo_listgroups_unix.go + cgo_lookup_unix.go + lookup.go + user.go + ) + +IF (CGO_ENABLED) + CGO_SRCS( + cgo_lookup_cgo.go + getgrouplist_unix.go + ) +ENDIF() +ELSEIF (OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + listgroups_unix.go + lookup.go + lookup_stubs.go + lookup_unix.go + user.go + ) +ELSEIF (OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + lookup.go + lookup_windows.go + user.go + ) +ENDIF() +END() |