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