golang.zx2c4.com/wireguard/windows@v0.5.4-0.20230123132234-dcc0eb72a04b/updater/msirunner.go (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved. 4 */ 5 6 package updater 7 8 import ( 9 "crypto/rand" 10 "encoding/hex" 11 "errors" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "runtime" 16 "syscall" 17 "unsafe" 18 19 "golang.org/x/sys/windows" 20 ) 21 22 type tempFile struct { 23 *os.File 24 originalHandle windows.Handle 25 } 26 27 func (t *tempFile) ExclusivePath() string { 28 if t.originalHandle != 0 { 29 t.Close() // TODO: sort of a toctou, but msi requires unshared file 30 t.originalHandle = 0 31 } 32 return t.Name() 33 } 34 35 func (t *tempFile) Delete() error { 36 if t.originalHandle == 0 { 37 name16, err := windows.UTF16PtrFromString(t.Name()) 38 if err != nil { 39 return err 40 } 41 return windows.DeleteFile(name16) // TODO: how does this deal with reparse points? 42 } 43 disposition := byte(1) 44 err := windows.SetFileInformationByHandle(t.originalHandle, windows.FileDispositionInfo, &disposition, 1) 45 t.originalHandle = 0 46 t.Close() 47 return err 48 } 49 50 func runMsi(msi *tempFile, userToken uintptr) error { 51 system32, err := windows.GetSystemDirectory() 52 if err != nil { 53 return err 54 } 55 devNull, err := os.OpenFile(os.DevNull, os.O_RDWR, 0) 56 if err != nil { 57 return err 58 } 59 defer devNull.Close() 60 msiPath := msi.ExclusivePath() 61 attr := &os.ProcAttr{ 62 Sys: &syscall.SysProcAttr{ 63 Token: syscall.Token(userToken), 64 }, 65 Files: []*os.File{devNull, devNull, devNull}, 66 Dir: filepath.Dir(msiPath), 67 } 68 msiexec := filepath.Join(system32, "msiexec.exe") 69 proc, err := os.StartProcess(msiexec, []string{msiexec, "/qb!-", "/i", filepath.Base(msiPath)}, attr) 70 if err != nil { 71 return err 72 } 73 state, err := proc.Wait() 74 if err != nil { 75 return err 76 } 77 if !state.Success() { 78 return &exec.ExitError{ProcessState: state} 79 } 80 return nil 81 } 82 83 func msiTempFile() (*tempFile, error) { 84 var randBytes [32]byte 85 n, err := rand.Read(randBytes[:]) 86 if err != nil { 87 return nil, err 88 } 89 if n != int(len(randBytes)) { 90 return nil, errors.New("Unable to generate random bytes") 91 } 92 sd, err := windows.SecurityDescriptorFromString("O:SYD:PAI(A;;FA;;;SY)(A;;FR;;;BA)") 93 if err != nil { 94 return nil, err 95 } 96 sa := &windows.SecurityAttributes{ 97 Length: uint32(unsafe.Sizeof(windows.SecurityAttributes{})), 98 SecurityDescriptor: sd, 99 } 100 windir, err := windows.GetWindowsDirectory() 101 if err != nil { 102 return nil, err 103 } 104 name := filepath.Join(windir, "Temp", hex.EncodeToString(randBytes[:])) 105 name16 := windows.StringToUTF16Ptr(name) 106 fileHandle, err := windows.CreateFile(name16, windows.GENERIC_WRITE|windows.DELETE, 0, sa, windows.CREATE_NEW, windows.FILE_ATTRIBUTE_TEMPORARY, 0) 107 runtime.KeepAlive(sd) 108 if err != nil { 109 return nil, err 110 } 111 windows.MoveFileEx(name16, nil, windows.MOVEFILE_DELAY_UNTIL_REBOOT) 112 return &tempFile{ 113 File: os.NewFile(uintptr(fileHandle), name), 114 originalHandle: fileHandle, 115 }, nil 116 }