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  }