1// Copyright 2018 Keybase, Inc. All rights reserved. Use of 2// this source code is governed by the included BSD license. 3 4// +build windows 5 6package libkb 7 8import ( 9 "syscall" 10 "unsafe" 11 12 "github.com/keybase/client/go/logger" 13 "golang.org/x/sys/windows" 14) 15 16var ( 17 modkernel32 = windows.NewLazySystemDLL("kernel32.dll") 18 procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") 19) 20 21const ERROR_PIPE_BUSY = 231 22 23type _PipeBusyError struct{} 24 25var PipeBusyError _PipeBusyError 26 27func (e _PipeBusyError) Error() string { 28 return "All pipe instances are busy" 29} 30 31func waitNamedPipe(name string, timeout uint32) (err error) { 32 rawName, e1 := syscall.UTF16PtrFromString(name) 33 if e1 != nil { 34 return e1 35 } 36 37 r1, _, e2 := procWaitNamedPipeW.Call(uintptr(unsafe.Pointer(rawName)), uintptr(timeout)) 38 if r1 == 0 { 39 return e2 40 } 41 return 42} 43 44// currentProcessUserSid is a utility to get the 45// SID of the current user running the process. 46func currentProcessUserSid() (*windows.SID, error) { 47 tok, err := windows.OpenCurrentProcessToken() 48 if err != nil { 49 return nil, err 50 } 51 defer tok.Close() 52 tokUser, err := tok.GetTokenUser() 53 if err != nil { 54 return nil, err 55 } 56 return (*windows.SID)(tokUser.User.Sid), nil 57} 58 59// currentProcessUserSid is a utility to get the 60// SID of the named pipe 61func GetFileUserSid(name string) (*windows.SID, error) { 62 var userSID *windows.SID 63 var secDesc windows.Handle 64 65 err := GetNamedSecurityInfo(name, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &userSID, nil, nil, nil, &secDesc) 66 if err != nil { 67 return nil, err 68 } 69 return userSID, nil 70} 71 72type AccountInfo struct { 73 Account string `json:"account"` 74 Domain string `json:"domain"` 75 Type uint32 `json:"type"` 76 SID string `json:"SID"` 77 Err error `json:"error"` 78} 79 80type PipeOwnerInfo struct { 81 IsOwner bool `json:"isOwner"` 82 PipeAccount AccountInfo `json:"pipe"` 83 UserAccount AccountInfo `json:"user"` 84} 85 86func IsPipeowner(log logger.Logger, name string) (owner PipeOwnerInfo, err error) { 87 log.Debug("+ IsPipeowner(%s)", name) 88 defer func() { 89 log.Debug("- IsPiperowner -> (%v, %v)", owner, err) 90 }() 91 userSid, err := currentProcessUserSid() 92 if err != nil { 93 return owner, err 94 } 95 96 pipeSid, err := GetFileUserSid(name) 97 if err == PipeBusyError { 98 // If at least one instance of the pipe has been created, this function 99 // will wait timeout milliseconds for it to become available. 100 // It will return immediately regardless of timeout, if no instances 101 // of the named pipe have been created yet. 102 // If this returns with no error, there is a pipe available. 103 err2 := waitNamedPipe(name, 1000) 104 if err2 != nil { 105 return owner, err // return original busy error 106 } 107 pipeSid, err = GetFileUserSid(name) 108 } 109 if err != nil { 110 return owner, err 111 } 112 owner.IsOwner = windows.EqualSid(pipeSid, userSid) 113 owner.PipeAccount.Account, owner.PipeAccount.Domain, owner.PipeAccount.Type, owner.PipeAccount.Err = pipeSid.LookupAccount("") 114 owner.PipeAccount.SID, err = pipeSid.String() 115 if err != nil { 116 log.Errorf("error getting owner SID: %s", err.Error()) 117 } 118 owner.UserAccount.Account, owner.UserAccount.Domain, owner.UserAccount.Type, owner.UserAccount.Err = userSid.LookupAccount("") 119 owner.UserAccount.SID, err = userSid.String() 120 if err != nil { 121 log.Errorf("error getting user SID: %s", err.Error()) 122 } 123 124 if !owner.IsOwner { 125 // If the pipe is served by an admin, let local security policies control access 126 // https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems 127 if owner.PipeAccount.SID == "S-1-5-32-544" && owner.PipeAccount.Type == syscall.SidTypeAlias { 128 owner.IsOwner = true 129 } 130 } 131 log.Debug("%v", owner) 132 return owner, nil 133} 134