|
|
@@ -12,6 +12,7 @@ import (
|
|
|
"net/netip"
|
|
|
"os"
|
|
|
"os/exec"
|
|
|
+ "path/filepath"
|
|
|
"slices"
|
|
|
"strings"
|
|
|
"sync"
|
|
|
@@ -155,6 +156,13 @@ type firewallTweaker struct {
|
|
|
// stop makes fwProc exit when closed.
|
|
|
fwProcWriter io.WriteCloser
|
|
|
fwProcEncoder *json.Encoder
|
|
|
+
|
|
|
+ // The path to the 'netsh.exe' binary, populated during the first call
|
|
|
+ // to runFirewall.
|
|
|
+ //
|
|
|
+ // not protected by mu; netshPath is only mutated inside netshPathOnce
|
|
|
+ netshPathOnce sync.Once
|
|
|
+ netshPath string
|
|
|
}
|
|
|
|
|
|
func (ft *firewallTweaker) clear() { ft.set(nil, nil, nil) }
|
|
|
@@ -185,10 +193,43 @@ func (ft *firewallTweaker) set(cidrs []string, routes, localRoutes []netip.Prefi
|
|
|
go ft.doAsyncSet()
|
|
|
}
|
|
|
|
|
|
+// getNetshPath returns the path that should be used to execute netsh.
|
|
|
+//
|
|
|
+// We've seen a report from a customer that we're triggering the "cannot run
|
|
|
+// executable found relative to current directory" protection that was added to
|
|
|
+// prevent running possibly attacker-controlled binaries. To mitigate this,
|
|
|
+// first try looking up the path to netsh.exe in the System32 directory
|
|
|
+// explicitly, and then fall back to the prior behaviour of passing "netsh" to
|
|
|
+// os/exec.Command.
|
|
|
+func (ft *firewallTweaker) getNetshPath() string {
|
|
|
+ ft.netshPathOnce.Do(func() {
|
|
|
+ // The default value is the old approach: just run "netsh" and
|
|
|
+ // let os/exec resolve that into a full path.
|
|
|
+ ft.netshPath = "netsh"
|
|
|
+
|
|
|
+ path, err := windows.KnownFolderPath(windows.FOLDERID_System, 0)
|
|
|
+ if err != nil {
|
|
|
+ ft.logf("getNetshPath: error getting FOLDERID_System: %v", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ expath := filepath.Join(path, "netsh.exe")
|
|
|
+ if _, err := os.Stat(expath); err == nil {
|
|
|
+ ft.netshPath = expath
|
|
|
+ return
|
|
|
+ } else if !os.IsNotExist(err) {
|
|
|
+ ft.logf("getNetshPath: error checking for existence of %q: %v", expath, err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Keep default
|
|
|
+ })
|
|
|
+ return ft.netshPath
|
|
|
+}
|
|
|
+
|
|
|
func (ft *firewallTweaker) runFirewall(args ...string) (time.Duration, error) {
|
|
|
t0 := time.Now()
|
|
|
args = append([]string{"advfirewall", "firewall"}, args...)
|
|
|
- cmd := exec.Command("netsh", args...)
|
|
|
+ cmd := exec.Command(ft.getNetshPath(), args...)
|
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
|
|
b, err := cmd.CombinedOutput()
|
|
|
if err != nil {
|