tailscale/util/winutil/gp/gp_windows.go
Nick Khyl 8bd442ba8c util/winutil/gp, net/dns: add package for Group Policy API
This adds a package with GP-related functions and types to be used in the future PRs.
It also updates nrptRuleDatabase to use the new package instead of its own gpNotificationWatcher implementation.

Updates #12687

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2024-07-08 20:37:03 -05:00

80 lines
2.7 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Package gp contains [Group Policy]-related functions and types.
//
// [Group Policy]: https://web.archive.org/web/20240630210707/https://learn.microsoft.com/en-us/previous-versions/windows/desktop/policy/group-policy-start-page
package gp
import (
"fmt"
"runtime"
"golang.org/x/sys/windows"
)
// Scope is a user or machine policy scope.
type Scope int
const (
// MachinePolicy indicates a machine policy.
// Registry-based machine policies reside in HKEY_LOCAL_MACHINE.
MachinePolicy Scope = iota
// UserPolicy indicates a user policy.
// Registry-based user policies reside in HKEY_CURRENT_USER of the corresponding user.
UserPolicy
)
// _RP_FORCE causes RefreshPolicyEx to reapply policy even if no policy change was detected.
// See [RP_FORCE] for details.
//
// [RP_FORCE]: https://web.archive.org/save/https://learn.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-refreshpolicyex
const _RP_FORCE = 0x1
// RefreshUserPolicy triggers a machine policy refresh, but does not wait for it to complete.
// When the force parameter is true, it causes the Group Policy to reapply policy even
// if no policy change was detected.
func RefreshMachinePolicy(force bool) error {
return refreshPolicyEx(true, toRefreshPolicyFlags(force))
}
// RefreshUserPolicy triggers a user policy refresh, but does not wait for it to complete.
// When the force parameter is true, it causes the Group Policy to reapply policy even
// if no policy change was detected.
//
// The token indicates user whose policy should be refreshed.
// If specified, the token must be either a primary token with TOKEN_QUERY and TOKEN_DUPLICATE
// access, or an impersonation token with TOKEN_QUERY and TOKEN_IMPERSONATE access,
// and the specified user must be logged in interactively.
//
// Otherwise, a zero token value indicates the current user. It should not
// be used by services or other applications running under system identities.
//
// The function fails with windows.ERROR_ACCESS_DENIED if the user represented by the token
// is not logged in interactively at the time of the call.
func RefreshUserPolicy(token windows.Token, force bool) error {
if token != 0 {
// Impersonate the user whose policy we need to refresh.
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if err := impersonateLoggedOnUser(token); err != nil {
return err
}
defer func() {
if err := windows.RevertToSelf(); err != nil {
// RevertToSelf errors are non-recoverable.
panic(fmt.Errorf("could not revert impersonation: %w", err))
}
}()
}
return refreshPolicyEx(true, toRefreshPolicyFlags(force))
}
func toRefreshPolicyFlags(force bool) uint32 {
if force {
return _RP_FORCE
}
return 0
}