Home | History | Annotate | Download | only in user
      1 // Copyright 2012 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 package user
      6 
      7 import (
      8 	"errors"
      9 	"fmt"
     10 	"syscall"
     11 	"unsafe"
     12 )
     13 
     14 func init() {
     15 	groupImplemented = false
     16 }
     17 
     18 func isDomainJoined() (bool, error) {
     19 	var domain *uint16
     20 	var status uint32
     21 	err := syscall.NetGetJoinInformation(nil, &domain, &status)
     22 	if err != nil {
     23 		return false, err
     24 	}
     25 	syscall.NetApiBufferFree((*byte)(unsafe.Pointer(domain)))
     26 	return status == syscall.NetSetupDomainName, nil
     27 }
     28 
     29 func lookupFullNameDomain(domainAndUser string) (string, error) {
     30 	return syscall.TranslateAccountName(domainAndUser,
     31 		syscall.NameSamCompatible, syscall.NameDisplay, 50)
     32 }
     33 
     34 func lookupFullNameServer(servername, username string) (string, error) {
     35 	s, e := syscall.UTF16PtrFromString(servername)
     36 	if e != nil {
     37 		return "", e
     38 	}
     39 	u, e := syscall.UTF16PtrFromString(username)
     40 	if e != nil {
     41 		return "", e
     42 	}
     43 	var p *byte
     44 	e = syscall.NetUserGetInfo(s, u, 10, &p)
     45 	if e != nil {
     46 		return "", e
     47 	}
     48 	defer syscall.NetApiBufferFree(p)
     49 	i := (*syscall.UserInfo10)(unsafe.Pointer(p))
     50 	if i.FullName == nil {
     51 		return "", nil
     52 	}
     53 	name := syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(i.FullName))[:])
     54 	return name, nil
     55 }
     56 
     57 func lookupFullName(domain, username, domainAndUser string) (string, error) {
     58 	joined, err := isDomainJoined()
     59 	if err == nil && joined {
     60 		name, err := lookupFullNameDomain(domainAndUser)
     61 		if err == nil {
     62 			return name, nil
     63 		}
     64 	}
     65 	name, err := lookupFullNameServer(domain, username)
     66 	if err == nil {
     67 		return name, nil
     68 	}
     69 	// domain worked neither as a domain nor as a server
     70 	// could be domain server unavailable
     71 	// pretend username is fullname
     72 	return username, nil
     73 }
     74 
     75 func newUser(usid *syscall.SID, gid, dir string) (*User, error) {
     76 	username, domain, t, e := usid.LookupAccount("")
     77 	if e != nil {
     78 		return nil, e
     79 	}
     80 	if t != syscall.SidTypeUser {
     81 		return nil, fmt.Errorf("user: should be user account type, not %d", t)
     82 	}
     83 	domainAndUser := domain + `\` + username
     84 	uid, e := usid.String()
     85 	if e != nil {
     86 		return nil, e
     87 	}
     88 	name, e := lookupFullName(domain, username, domainAndUser)
     89 	if e != nil {
     90 		return nil, e
     91 	}
     92 	u := &User{
     93 		Uid:      uid,
     94 		Gid:      gid,
     95 		Username: domainAndUser,
     96 		Name:     name,
     97 		HomeDir:  dir,
     98 	}
     99 	return u, nil
    100 }
    101 
    102 func current() (*User, error) {
    103 	t, e := syscall.OpenCurrentProcessToken()
    104 	if e != nil {
    105 		return nil, e
    106 	}
    107 	defer t.Close()
    108 	u, e := t.GetTokenUser()
    109 	if e != nil {
    110 		return nil, e
    111 	}
    112 	pg, e := t.GetTokenPrimaryGroup()
    113 	if e != nil {
    114 		return nil, e
    115 	}
    116 	gid, e := pg.PrimaryGroup.String()
    117 	if e != nil {
    118 		return nil, e
    119 	}
    120 	dir, e := t.GetUserProfileDirectory()
    121 	if e != nil {
    122 		return nil, e
    123 	}
    124 	return newUser(u.User.Sid, gid, dir)
    125 }
    126 
    127 // BUG(brainman): Lookup and LookupId functions do not set
    128 // Gid and HomeDir fields in the User struct returned on windows.
    129 
    130 func newUserFromSid(usid *syscall.SID) (*User, error) {
    131 	// TODO(brainman): do not know where to get gid and dir fields
    132 	gid := "unknown"
    133 	dir := "Unknown directory"
    134 	return newUser(usid, gid, dir)
    135 }
    136 
    137 func lookupUser(username string) (*User, error) {
    138 	sid, _, t, e := syscall.LookupSID("", username)
    139 	if e != nil {
    140 		return nil, e
    141 	}
    142 	if t != syscall.SidTypeUser {
    143 		return nil, fmt.Errorf("user: should be user account type, not %d", t)
    144 	}
    145 	return newUserFromSid(sid)
    146 }
    147 
    148 func lookupUserId(uid string) (*User, error) {
    149 	sid, e := syscall.StringToSid(uid)
    150 	if e != nil {
    151 		return nil, e
    152 	}
    153 	return newUserFromSid(sid)
    154 }
    155 
    156 func lookupGroup(groupname string) (*Group, error) {
    157 	return nil, errors.New("user: LookupGroup not implemented on windows")
    158 }
    159 
    160 func lookupGroupId(string) (*Group, error) {
    161 	return nil, errors.New("user: LookupGroupId not implemented on windows")
    162 }
    163 
    164 func listGroups(*User) ([]string, error) {
    165 	return nil, errors.New("user: GroupIds not implemented on windows")
    166 }
    167