github.com/elastic/gosigar@v0.14.3/sys/windows/privileges.go (about)

     1  // +build windows
     2  
     3  package windows
     4  
     5  import (
     6  	"bytes"
     7  	"encoding/binary"
     8  	"encoding/json"
     9  	"fmt"
    10  	"runtime"
    11  	"strings"
    12  	"sync"
    13  	"syscall"
    14  
    15  	"github.com/pkg/errors"
    16  	"golang.org/x/sys/windows"
    17  )
    18  
    19  // Cache of privilege names to LUIDs.
    20  var (
    21  	privNames     = make(map[string]int64)
    22  	privNameMutex sync.Mutex
    23  )
    24  
    25  const (
    26  	// SeDebugPrivilege is the name of the privilege used to debug programs.
    27  	SeDebugPrivilege = "SeDebugPrivilege"
    28  )
    29  
    30  // Errors returned by AdjustTokenPrivileges.
    31  const (
    32  	ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
    33  )
    34  
    35  // Attribute bits for privileges.
    36  const (
    37  	_SE_PRIVILEGE_ENABLED_BY_DEFAULT uint32 = 0x00000001
    38  	_SE_PRIVILEGE_ENABLED            uint32 = 0x00000002
    39  	_SE_PRIVILEGE_REMOVED            uint32 = 0x00000004
    40  	_SE_PRIVILEGE_USED_FOR_ACCESS    uint32 = 0x80000000
    41  )
    42  
    43  // Privilege contains information about a single privilege associated with a
    44  // Token.
    45  type Privilege struct {
    46  	LUID             int64  `json:"-"` // Locally unique identifier (guaranteed only until the system is restarted).
    47  	Name             string `json:"-"`
    48  	EnabledByDefault bool   `json:"enabled_by_default,omitempty"`
    49  	Enabled          bool   `json:"enabled"`
    50  	Removed          bool   `json:"removed,omitempty"`
    51  	Used             bool   `json:"used,omitempty"`
    52  }
    53  
    54  func (p Privilege) String() string {
    55  	var buf bytes.Buffer
    56  	buf.WriteString(p.Name)
    57  	buf.WriteString("=(")
    58  
    59  	opts := make([]string, 0, 4)
    60  	if p.EnabledByDefault {
    61  		opts = append(opts, "Default")
    62  	}
    63  	if p.Enabled {
    64  		opts = append(opts, "Enabled")
    65  	}
    66  	if !p.EnabledByDefault && !p.Enabled {
    67  		opts = append(opts, "Disabled")
    68  	}
    69  	if p.Removed {
    70  		opts = append(opts, "Removed")
    71  	}
    72  	if p.Used {
    73  		opts = append(opts, "Used")
    74  	}
    75  
    76  	buf.WriteString(strings.Join(opts, ", "))
    77  	buf.WriteString(")")
    78  
    79  	// Example: SeDebugPrivilege=(Default, Enabled)
    80  	return buf.String()
    81  }
    82  
    83  // User represent the information about a Windows account.
    84  type User struct {
    85  	SID     string
    86  	Account string
    87  	Domain  string
    88  	Type    uint32
    89  }
    90  
    91  func (u User) String() string {
    92  	return fmt.Sprintf(`User:%v\%v, SID:%v, Type:%v`, u.Domain, u.Account, u.SID, u.Type)
    93  }
    94  
    95  // DebugInfo contains general debug info about the current process.
    96  type DebugInfo struct {
    97  	OSVersion    Version              // OS version info.
    98  	Arch         string               // Architecture of the machine.
    99  	NumCPU       int                  // Number of CPUs.
   100  	User         User                 // User that this process is running as.
   101  	ProcessPrivs map[string]Privilege // Privileges held by the process.
   102  }
   103  
   104  func (d DebugInfo) String() string {
   105  	bytes, _ := json.Marshal(d)
   106  	return string(bytes)
   107  }
   108  
   109  // LookupPrivilegeName looks up a privilege name given a LUID value.
   110  func LookupPrivilegeName(systemName string, luid int64) (string, error) {
   111  	buf := make([]uint16, 256)
   112  	bufSize := uint32(len(buf))
   113  	err := _LookupPrivilegeName(systemName, &luid, &buf[0], &bufSize)
   114  	if err != nil {
   115  		return "", errors.Wrapf(err, "LookupPrivilegeName failed for luid=%v", luid)
   116  	}
   117  
   118  	return syscall.UTF16ToString(buf), nil
   119  }
   120  
   121  // mapPrivileges maps privilege names to LUID values.
   122  func mapPrivileges(names []string) ([]int64, error) {
   123  	var privileges []int64
   124  	privNameMutex.Lock()
   125  	defer privNameMutex.Unlock()
   126  	for _, name := range names {
   127  		p, ok := privNames[name]
   128  		if !ok {
   129  			err := _LookupPrivilegeValue("", name, &p)
   130  			if err != nil {
   131  				return nil, errors.Wrapf(err, "LookupPrivilegeValue failed on '%v'", name)
   132  			}
   133  			privNames[name] = p
   134  		}
   135  		privileges = append(privileges, p)
   136  	}
   137  	return privileges, nil
   138  }
   139  
   140  // EnableTokenPrivileges enables the specified privileges in the given
   141  // Token. The token must have TOKEN_ADJUST_PRIVILEGES access. If the token
   142  // does not already contain the privilege it cannot be enabled.
   143  func EnableTokenPrivileges(token syscall.Token, privileges ...string) error {
   144  	privValues, err := mapPrivileges(privileges)
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	var b bytes.Buffer
   150  	binary.Write(&b, binary.LittleEndian, uint32(len(privValues)))
   151  	for _, p := range privValues {
   152  		binary.Write(&b, binary.LittleEndian, p)
   153  		binary.Write(&b, binary.LittleEndian, uint32(_SE_PRIVILEGE_ENABLED))
   154  	}
   155  
   156  	success, err := _AdjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(b.Len()), nil, nil)
   157  	if !success {
   158  		return err
   159  	}
   160  	if err == ERROR_NOT_ALL_ASSIGNED {
   161  		return errors.Wrap(err, "error not all privileges were assigned")
   162  	}
   163  
   164  	return nil
   165  }
   166  
   167  // GetTokenPrivileges returns a list of privileges associated with a token.
   168  // The provided token must have at a minimum TOKEN_QUERY access. This is a
   169  // wrapper around the GetTokenInformation function.
   170  // https://msdn.microsoft.com/en-us/library/windows/desktop/aa446671(v=vs.85).aspx
   171  func GetTokenPrivileges(token syscall.Token) (map[string]Privilege, error) {
   172  	// Determine the required buffer size.
   173  	var size uint32
   174  	syscall.GetTokenInformation(token, syscall.TokenPrivileges, nil, 0, &size)
   175  
   176  	// This buffer will receive a TOKEN_PRIVILEGE structure.
   177  	b := bytes.NewBuffer(make([]byte, size))
   178  	err := syscall.GetTokenInformation(token, syscall.TokenPrivileges, &b.Bytes()[0], uint32(b.Len()), &size)
   179  	if err != nil {
   180  		return nil, errors.Wrap(err, "GetTokenInformation failed")
   181  	}
   182  
   183  	var privilegeCount uint32
   184  	err = binary.Read(b, binary.LittleEndian, &privilegeCount)
   185  	if err != nil {
   186  		return nil, errors.Wrap(err, "failed to read PrivilegeCount")
   187  	}
   188  
   189  	rtn := make(map[string]Privilege, privilegeCount)
   190  	for i := 0; i < int(privilegeCount); i++ {
   191  		var luid int64
   192  		err = binary.Read(b, binary.LittleEndian, &luid)
   193  		if err != nil {
   194  			return nil, errors.Wrap(err, "failed to read LUID value")
   195  		}
   196  
   197  		var attributes uint32
   198  		err = binary.Read(b, binary.LittleEndian, &attributes)
   199  		if err != nil {
   200  			return nil, errors.Wrap(err, "failed to read attributes")
   201  		}
   202  
   203  		name, err := LookupPrivilegeName("", luid)
   204  		if err != nil {
   205  			return nil, errors.Wrapf(err, "LookupPrivilegeName failed for LUID=%v", luid)
   206  		}
   207  
   208  		rtn[name] = Privilege{
   209  			LUID:             luid,
   210  			Name:             name,
   211  			EnabledByDefault: (attributes & _SE_PRIVILEGE_ENABLED_BY_DEFAULT) > 0,
   212  			Enabled:          (attributes & _SE_PRIVILEGE_ENABLED) > 0,
   213  			Removed:          (attributes & _SE_PRIVILEGE_REMOVED) > 0,
   214  			Used:             (attributes & _SE_PRIVILEGE_USED_FOR_ACCESS) > 0,
   215  		}
   216  	}
   217  
   218  	return rtn, nil
   219  }
   220  
   221  // GetTokenUser returns the User associated with the given Token.
   222  func GetTokenUser(token syscall.Token) (User, error) {
   223  	tokenUser, err := token.GetTokenUser()
   224  	if err != nil {
   225  		return User{}, errors.Wrap(err, "GetTokenUser failed")
   226  	}
   227  
   228  	var user User
   229  	user.SID, err = tokenUser.User.Sid.String()
   230  	if err != nil {
   231  		return user, errors.Wrap(err, "ConvertSidToStringSid failed")
   232  	}
   233  
   234  	user.Account, user.Domain, user.Type, err = tokenUser.User.Sid.LookupAccount("")
   235  	if err != nil {
   236  		return user, errors.Wrap(err, "LookupAccountSid failed")
   237  	}
   238  
   239  	return user, nil
   240  }
   241  
   242  // GetDebugInfo returns general debug info about the current process.
   243  func GetDebugInfo() (*DebugInfo, error) {
   244  	h, err := windows.GetCurrentProcess()
   245  	if err != nil {
   246  		return nil, err
   247  	}
   248  
   249  	var token syscall.Token
   250  	err = syscall.OpenProcessToken(syscall.Handle(h), syscall.TOKEN_QUERY, &token)
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  
   255  	privs, err := GetTokenPrivileges(token)
   256  	if err != nil {
   257  		return nil, err
   258  	}
   259  
   260  	user, err := GetTokenUser(token)
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  
   265  	return &DebugInfo{
   266  		User:         user,
   267  		ProcessPrivs: privs,
   268  		OSVersion:    GetWindowsVersion(),
   269  		Arch:         runtime.GOARCH,
   270  		NumCPU:       runtime.NumCPU(),
   271  	}, nil
   272  }