github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/src/os/user/lookup_windows.go (about)

     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  	"internal/syscall/windows"
    11  	"internal/syscall/windows/registry"
    12  	"syscall"
    13  	"unsafe"
    14  )
    15  
    16  func init() {
    17  	groupImplemented = false
    18  }
    19  
    20  func isDomainJoined() (bool, error) {
    21  	var domain *uint16
    22  	var status uint32
    23  	err := syscall.NetGetJoinInformation(nil, &domain, &status)
    24  	if err != nil {
    25  		return false, err
    26  	}
    27  	syscall.NetApiBufferFree((*byte)(unsafe.Pointer(domain)))
    28  	return status == syscall.NetSetupDomainName, nil
    29  }
    30  
    31  func lookupFullNameDomain(domainAndUser string) (string, error) {
    32  	return syscall.TranslateAccountName(domainAndUser,
    33  		syscall.NameSamCompatible, syscall.NameDisplay, 50)
    34  }
    35  
    36  func lookupFullNameServer(servername, username string) (string, error) {
    37  	s, e := syscall.UTF16PtrFromString(servername)
    38  	if e != nil {
    39  		return "", e
    40  	}
    41  	u, e := syscall.UTF16PtrFromString(username)
    42  	if e != nil {
    43  		return "", e
    44  	}
    45  	var p *byte
    46  	e = syscall.NetUserGetInfo(s, u, 10, &p)
    47  	if e != nil {
    48  		return "", e
    49  	}
    50  	defer syscall.NetApiBufferFree(p)
    51  	i := (*syscall.UserInfo10)(unsafe.Pointer(p))
    52  	if i.FullName == nil {
    53  		return "", nil
    54  	}
    55  	name := syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(i.FullName))[:])
    56  	return name, nil
    57  }
    58  
    59  func lookupFullName(domain, username, domainAndUser string) (string, error) {
    60  	joined, err := isDomainJoined()
    61  	if err == nil && joined {
    62  		name, err := lookupFullNameDomain(domainAndUser)
    63  		if err == nil {
    64  			return name, nil
    65  		}
    66  	}
    67  	name, err := lookupFullNameServer(domain, username)
    68  	if err == nil {
    69  		return name, nil
    70  	}
    71  	// domain worked neither as a domain nor as a server
    72  	// could be domain server unavailable
    73  	// pretend username is fullname
    74  	return username, nil
    75  }
    76  
    77  // getProfilesDirectory retrieves the path to the root directory
    78  // where user profiles are stored.
    79  func getProfilesDirectory() (string, error) {
    80  	n := uint32(100)
    81  	for {
    82  		b := make([]uint16, n)
    83  		e := windows.GetProfilesDirectory(&b[0], &n)
    84  		if e == nil {
    85  			return syscall.UTF16ToString(b), nil
    86  		}
    87  		if e != syscall.ERROR_INSUFFICIENT_BUFFER {
    88  			return "", e
    89  		}
    90  		if n <= uint32(len(b)) {
    91  			return "", e
    92  		}
    93  	}
    94  }
    95  
    96  // lookupUsernameAndDomain obtains username and domain for usid.
    97  func lookupUsernameAndDomain(usid *syscall.SID) (username, domain string, e error) {
    98  	username, domain, t, e := usid.LookupAccount("")
    99  	if e != nil {
   100  		return "", "", e
   101  	}
   102  	if t != syscall.SidTypeUser {
   103  		return "", "", fmt.Errorf("user: should be user account type, not %d", t)
   104  	}
   105  	return username, domain, nil
   106  }
   107  
   108  // findHomeDirInRegistry finds the user home path based on usid string
   109  func findHomeDirInRegistry(uid string) (dir string, e error) {
   110  	k, e := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\`+uid, registry.QUERY_VALUE)
   111  	if e != nil {
   112  		return "", e
   113  	}
   114  	defer k.Close()
   115  	dir, _, e = k.GetStringValue("ProfileImagePath")
   116  	if e != nil {
   117  		return "", e
   118  	}
   119  	return dir, nil
   120  }
   121  
   122  func newUser(uid, gid, dir, username, domain string) (*User, error) {
   123  	domainAndUser := domain + `\` + username
   124  	name, e := lookupFullName(domain, username, domainAndUser)
   125  	if e != nil {
   126  		return nil, e
   127  	}
   128  	u := &User{
   129  		Uid:      uid,
   130  		Gid:      gid,
   131  		Username: domainAndUser,
   132  		Name:     name,
   133  		HomeDir:  dir,
   134  	}
   135  	return u, nil
   136  }
   137  
   138  func current() (*User, error) {
   139  	t, e := syscall.OpenCurrentProcessToken()
   140  	if e != nil {
   141  		return nil, e
   142  	}
   143  	defer t.Close()
   144  	u, e := t.GetTokenUser()
   145  	if e != nil {
   146  		return nil, e
   147  	}
   148  	pg, e := t.GetTokenPrimaryGroup()
   149  	if e != nil {
   150  		return nil, e
   151  	}
   152  	uid, e := u.User.Sid.String()
   153  	if e != nil {
   154  		return nil, e
   155  	}
   156  	gid, e := pg.PrimaryGroup.String()
   157  	if e != nil {
   158  		return nil, e
   159  	}
   160  	dir, e := t.GetUserProfileDirectory()
   161  	if e != nil {
   162  		return nil, e
   163  	}
   164  	username, domain, e := lookupUsernameAndDomain(u.User.Sid)
   165  	if e != nil {
   166  		return nil, e
   167  	}
   168  	return newUser(uid, gid, dir, username, domain)
   169  }
   170  
   171  // TODO: The Gid field in the User struct is not set on Windows.
   172  
   173  func newUserFromSid(usid *syscall.SID) (*User, error) {
   174  	gid := "unknown"
   175  	username, domain, e := lookupUsernameAndDomain(usid)
   176  	if e != nil {
   177  		return nil, e
   178  	}
   179  	uid, e := usid.String()
   180  	if e != nil {
   181  		return nil, e
   182  	}
   183  	// if this user has logged at least once his home path should be stored
   184  	// in the registry under his SID. references:
   185  	// https://social.technet.microsoft.com/wiki/contents/articles/13895.how-to-remove-a-corrupted-user-profile-from-the-registry.aspx
   186  	// https://support.asperasoft.com/hc/en-us/articles/216127438-How-to-delete-Windows-user-profiles
   187  	//
   188  	// the registry is the most reliable way to find the home path as the user
   189  	// might have decided to move it outside of the default location
   190  	// (e.g. c:\users). reference:
   191  	// 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
   192  	dir, e := findHomeDirInRegistry(uid)
   193  	if e != nil {
   194  		// if the home path does not exists in the registry, the user might have
   195  		// not logged in yet; fall back to using getProfilesDirectory(). find the
   196  		// username based on a SID and append that to the result of
   197  		// getProfilesDirectory(). the domain is not of relevance here.
   198  		dir, e = getProfilesDirectory()
   199  		if e != nil {
   200  			return nil, e
   201  		}
   202  		dir += `\` + username
   203  	}
   204  	return newUser(uid, gid, dir, username, domain)
   205  }
   206  
   207  func lookupUser(username string) (*User, error) {
   208  	sid, _, t, e := syscall.LookupSID("", username)
   209  	if e != nil {
   210  		return nil, e
   211  	}
   212  	if t != syscall.SidTypeUser {
   213  		return nil, fmt.Errorf("user: should be user account type, not %d", t)
   214  	}
   215  	return newUserFromSid(sid)
   216  }
   217  
   218  func lookupUserId(uid string) (*User, error) {
   219  	sid, e := syscall.StringToSid(uid)
   220  	if e != nil {
   221  		return nil, e
   222  	}
   223  	return newUserFromSid(sid)
   224  }
   225  
   226  func lookupGroup(groupname string) (*Group, error) {
   227  	return nil, errors.New("user: LookupGroup not implemented on windows")
   228  }
   229  
   230  func lookupGroupId(string) (*Group, error) {
   231  	return nil, errors.New("user: LookupGroupId not implemented on windows")
   232  }
   233  
   234  func listGroups(*User) ([]string, error) {
   235  	return nil, errors.New("user: GroupIds not implemented on windows")
   236  }