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