github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbnm/hostmanifest/known_windows.go (about) 1 package hostmanifest 2 3 import ( 4 "encoding/json" 5 "os" 6 "path/filepath" 7 8 "golang.org/x/sys/windows/registry" 9 ) 10 11 // KnownInstallers returns a map of browser-to-Installer that this package 12 // knows about for this platform. 13 func KnownInstallers() map[string]Installer { 14 return map[string]Installer{ 15 "chrome": &whitelistRegistry{ 16 RegKey: `SOFTWARE\Google\Chrome\NativeMessagingHosts`, 17 FileSuffix: `_chrome`, 18 }, 19 "chromium": &whitelistRegistry{ 20 RegKey: `SOFTWARE\Chromium\NativeMessagingHosts`, 21 FileSuffix: `_chromium`, 22 }, 23 "firefox": &whitelistRegistry{ 24 RegKey: `SOFTWARE\Mozilla\NativeMessagingHosts`, 25 FileSuffix: `_firefox`, 26 }, 27 } 28 } 29 30 // whitelistRegistry is used for installing the whitelist as a JSON into a given 31 // registry entry. When Install is called, it will also write a JSON manifest to be adjacent to the app Path. 32 type whitelistRegistry struct { 33 // RegKey is the path of the NativeMessage whitelist registry key 34 RegKey string 35 // FileSuffix is a suffix for the JSON filename before the extension to 36 // avoid collisions across browsers because they're written in the same 37 // directory. 38 FileSuffix string 39 } 40 41 // writeJSON writes the whitelist manifest JSON file adjacent to the app's 42 // binary. 43 func (w *whitelistRegistry) writeJSON(path string, app AppManifest) error { 44 // Write the file 45 fp, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) 46 if err != nil { 47 return err 48 } 49 defer fp.Close() 50 51 encoder := json.NewEncoder(fp) 52 encoder.SetIndent("", " ") 53 if err := encoder.Encode(&app); err != nil { 54 return err 55 } 56 57 return fp.Sync() 58 } 59 60 func (w *whitelistRegistry) paths(app AppManifest) (jsonPath string, keyPath string) { 61 parentDir := filepath.Dir(app.BinPath()) 62 suffix := filepath.Base(w.FileSuffix) // To make sure it's safe to use 63 basename := app.ID() 64 if suffix != "." { 65 basename += suffix 66 } 67 68 jsonPath = filepath.Join(parentDir, basename+".json") 69 keyPath = filepath.Join(w.RegKey, app.ID()) 70 return 71 } 72 73 // Install on Windows ignores the provided user and always installs in the 74 // CURRET_USER context, writing the JSON adjacent to the binary path. 75 func (w *whitelistRegistry) Install(_ User, app AppManifest) error { 76 // We assume that the parentDir already exists, where the binary lives. 77 jsonPath, keyPath := w.paths(app) 78 if err := w.writeJSON(jsonPath, app); err != nil { 79 return err 80 } 81 82 scope := registry.CURRENT_USER 83 k, _, err := registry.CreateKey(scope, keyPath, registry.SET_VALUE|registry.CREATE_SUB_KEY|registry.WRITE) 84 if err != nil { 85 return err 86 } 87 defer k.Close() 88 89 return k.SetStringValue("", jsonPath) 90 } 91 92 func (w *whitelistRegistry) Uninstall(_ User, app AppManifest) error { 93 scope := registry.CURRENT_USER 94 jsonPath, keyPath := w.paths(app) 95 if err := registry.DeleteKey(scope, keyPath); err != nil { 96 return err 97 } 98 if err := os.Remove(jsonPath); err != nil && !os.IsNotExist(err) { 99 // We don't care if it doesn't exist, but other errors should escalate 100 return err 101 } 102 return nil 103 }