tailscale/ipn/ipnlocal/profiles_windows.go
Adrian Dewhurst 32194cdc70 ipn/ipnlocal: add new DNS and subnet router policies
In addition to the new policy keys for the new options, some
already-in-use but missing policy keys are also being added to
util/syspolicy.

Updates ENG-2133

Change-Id: Iad08ca47f839ea6a65f81b76b4f9ef21183ebdc6
Signed-off-by: Adrian Dewhurst <adrian@tailscale.com>
2023-10-24 14:33:59 -04:00

102 lines
3.2 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package ipnlocal
import (
"errors"
"fmt"
"io/fs"
"os"
"os/user"
"path/filepath"
"tailscale.com/atomicfile"
"tailscale.com/ipn"
"tailscale.com/util/syspolicy"
"tailscale.com/util/winutil/policy"
)
const (
legacyPrefsFile = "prefs"
legacyPrefsMigrationSentinelFile = "_migrated-to-profiles"
legacyPrefsExt = ".conf"
)
func legacyPrefsDir(uid ipn.WindowsUserID) (string, error) {
// TODO(aaron): Ideally we'd have the impersonation token for the pipe's
// client and use it to call SHGetKnownFolderPath, thus yielding the correct
// path without having to make gross assumptions about directory names.
usr, err := user.LookupId(string(uid))
if err != nil {
return "", err
}
if usr.HomeDir == "" {
return "", fmt.Errorf("user %q does not have a home directory", uid)
}
userLegacyPrefsDir := filepath.Join(usr.HomeDir, "AppData", "Local", "Tailscale")
return userLegacyPrefsDir, nil
}
func (pm *profileManager) loadLegacyPrefs() (string, ipn.PrefsView, error) {
userLegacyPrefsDir, err := legacyPrefsDir(pm.currentUserID)
if err != nil {
pm.dlogf("no legacy preferences directory for %q: %v", pm.currentUserID, err)
return "", ipn.PrefsView{}, err
}
migrationSentinel := filepath.Join(userLegacyPrefsDir, legacyPrefsMigrationSentinelFile+legacyPrefsExt)
// verify that migration sentinel is not present
_, err = os.Stat(migrationSentinel)
if err == nil {
pm.dlogf("migration sentinel %q already exists", migrationSentinel)
return "", ipn.PrefsView{}, errAlreadyMigrated
}
if !os.IsNotExist(err) {
pm.dlogf("os.Stat(%q) = %v", migrationSentinel, err)
return "", ipn.PrefsView{}, err
}
prefsPath := filepath.Join(userLegacyPrefsDir, legacyPrefsFile+legacyPrefsExt)
prefs, err := ipn.LoadPrefs(prefsPath)
pm.dlogf("ipn.LoadPrefs(%q) = %v, %v", prefsPath, prefs, err)
if errors.Is(err, fs.ErrNotExist) {
return "", ipn.PrefsView{}, errAlreadyMigrated
}
if err != nil {
return "", ipn.PrefsView{}, err
}
prefs.ControlURL = policy.SelectControlURL(defaultPrefs.ControlURL(), prefs.ControlURL)
prefs.ExitNodeIP = resolveExitNodeIP(prefs.ExitNodeIP)
prefs.ShieldsUp = resolveShieldsUp(prefs.ShieldsUp)
prefs.ForceDaemon = resolveForceDaemon(prefs.ForceDaemon)
prefs.CorpDNS, _ = resolveOptionPolicy(syspolicy.EnableTailscaleDNS, prefs.CorpDNS)
prefs.RouteAll, _ = resolveOptionPolicy(syspolicy.EnableTailscaleSubnets, prefs.RouteAll)
pm.logf("migrating Windows profile to new format")
return migrationSentinel, prefs.View(), nil
}
func (pm *profileManager) completeMigration(migrationSentinel string) {
atomicfile.WriteFile(migrationSentinel, []byte{}, 0600)
}
func resolveShieldsUp(defval bool) bool {
pol := policy.GetPreferenceOptionPolicy("AllowIncomingConnections")
return !pol.ShouldEnable(!defval)
}
func resolveForceDaemon(defval bool) bool {
pol := policy.GetPreferenceOptionPolicy("UnattendedMode")
return pol.ShouldEnable(defval)
}
func resolveOptionPolicy(key syspolicy.Key, defval bool) (bool, error) {
pol, err := syspolicy.GetPreferenceOption(key)
if err != nil {
return defval, err
}
return pol.ShouldEnable(defval), nil
}