aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/updater
diff options
context:
space:
mode:
Diffstat (limited to 'updater')
-rw-r--r--updater/downloader.go19
-rw-r--r--updater/msirunner_windows.go52
2 files changed, 50 insertions, 21 deletions
diff --git a/updater/downloader.go b/updater/downloader.go
index fad9534e..bc4a8a7a 100644
--- a/updater/downloader.go
+++ b/updater/downloader.go
@@ -12,7 +12,6 @@ import (
"hash"
"io"
"net/http"
- "os"
"sync/atomic"
"golang.org/x/crypto/blake2b"
@@ -108,11 +107,7 @@ func DownloadVerifyAndExecute(userToken uintptr) (progress chan DownloadProgress
progress <- DownloadProgress{Activity: fmt.Sprintf("Msi destination is %#q", file.Name())}
defer func() {
if file != nil {
- name := file.Name()
- file.Seek(0, io.SeekStart)
- file.Truncate(0)
- file.Close()
- os.Remove(name) // TODO: Do we have any sort of TOCTOU here?
+ file.Delete()
}
}()
@@ -151,22 +146,14 @@ func DownloadVerifyAndExecute(userToken uintptr) (progress chan DownloadProgress
return
}
- // TODO: it would be nice to rename in place from "file.msi.unverified" to "file.msi", but Windows TOCTOU stuff
- // is hard, so we'll come back to this later.
- name := file.Name()
- file.Close()
- file = nil
-
progress <- DownloadProgress{Activity: "Verifying authenticode signature"}
- if !version.VerifyAuthenticode(name) {
- os.Remove(name) // TODO: Do we have any sort of TOCTOU here?
+ if !version.VerifyAuthenticode(file.ExclusivePath()) {
progress <- DownloadProgress{Error: errors.New("The downloaded update does not have an authentic authenticode signature")}
return
}
progress <- DownloadProgress{Activity: "Installing update"}
- err = runMsi(name, userToken)
- os.Remove(name) // TODO: Do we have any sort of TOCTOU here?
+ err = runMsi(file, userToken)
if err != nil {
progress <- DownloadProgress{Error: err}
return
diff --git a/updater/msirunner_windows.go b/updater/msirunner_windows.go
index f7ee35a2..e016dc78 100644
--- a/updater/msirunner_windows.go
+++ b/updater/msirunner_windows.go
@@ -19,7 +19,46 @@ import (
"golang.org/x/sys/windows"
)
-func runMsi(msiPath string, userToken uintptr) error {
+type tempFile struct {
+ *os.File
+ originalHandle windows.Handle
+}
+
+func (t *tempFile) ExclusivePath() string {
+ if t.originalHandle != 0 {
+ t.Close() // TODO: sort of a toctou, but msi requires unshared file
+ t.originalHandle = 0
+ }
+ return t.Name()
+}
+
+//TODO: remove when https://go-review.googlesource.com/c/sys/+/270757 is merged
+const fileDispositionInfo = 4
+
+func setFileInformationByHandle(handle windows.Handle, class uint32, inBuffer *byte, inBufferLen uint32) (err error) {
+ r1, _, e1 := syscall.Syscall6(windows.NewLazySystemDLL("kernel32.dll").NewProc("SetFileInformationByHandle").Addr(), 4, uintptr(handle), uintptr(class), uintptr(unsafe.Pointer(inBuffer)), uintptr(inBufferLen), 0, 0)
+ if r1 == 0 {
+ err = e1
+ }
+ return
+}
+
+func (t *tempFile) Delete() error {
+ if t.originalHandle == 0 {
+ name16, err := windows.UTF16PtrFromString(t.Name())
+ if err != nil {
+ return err
+ }
+ return windows.DeleteFile(name16) //TODO: how does this deal with reparse points?
+ }
+ disposition := uint32(1)
+ err := setFileInformationByHandle(t.originalHandle, fileDispositionInfo, (*byte)(unsafe.Pointer(&disposition)), uint32(unsafe.Sizeof(disposition)))
+ t.originalHandle = 0
+ t.Close()
+ return err
+}
+
+func runMsi(msi *tempFile, userToken uintptr) error {
system32, err := windows.GetSystemDirectory()
if err != nil {
return err
@@ -29,6 +68,7 @@ func runMsi(msiPath string, userToken uintptr) error {
return err
}
defer devNull.Close()
+ msiPath := msi.ExclusivePath()
attr := &os.ProcAttr{
Sys: &syscall.SysProcAttr{
Token: syscall.Token(userToken),
@@ -51,7 +91,7 @@ func runMsi(msiPath string, userToken uintptr) error {
return nil
}
-func msiTempFile() (*os.File, error) {
+func msiTempFile() (*tempFile, error) {
var randBytes [32]byte
n, err := rand.Read(randBytes[:])
if err != nil {
@@ -74,12 +114,14 @@ func msiTempFile() (*os.File, error) {
}
name := filepath.Join(windir, "Temp", hex.EncodeToString(randBytes[:]))
name16 := windows.StringToUTF16Ptr(name)
- // TODO: it would be nice to specify delete_on_close, but msiexec.exe doesn't open its files with read sharing.
- fileHandle, err := windows.CreateFile(name16, windows.GENERIC_WRITE, windows.FILE_SHARE_READ, sa, windows.CREATE_NEW, windows.FILE_ATTRIBUTE_NORMAL, 0)
+ fileHandle, err := windows.CreateFile(name16, windows.GENERIC_WRITE|windows.DELETE, 0, sa, windows.CREATE_NEW, windows.FILE_ATTRIBUTE_TEMPORARY, 0)
runtime.KeepAlive(sd)
if err != nil {
return nil, err
}
windows.MoveFileEx(name16, nil, windows.MOVEFILE_DELAY_UNTIL_REBOOT)
- return os.NewFile(uintptr(fileHandle), name), nil
+ return &tempFile{
+ File: os.NewFile(uintptr(fileHandle), name),
+ originalHandle: fileHandle,
+ }, nil
}