wip: migrate to mono-repo. SPN has already been moved to spn/
This commit is contained in:
257
spn/access/zones.go
Normal file
257
spn/access/zones.go
Normal file
@@ -0,0 +1,257 @@
|
||||
package access
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/jess/lhash"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/spn/access/token"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
)
|
||||
|
||||
var (
|
||||
// ExpandAndConnectZones are the zones that grant access to the expand and
|
||||
// connect operations.
|
||||
ExpandAndConnectZones = []string{"pblind1", "alpha2", "fallback1"}
|
||||
|
||||
zonePermissions = map[string]terminal.Permission{
|
||||
"pblind1": terminal.AddPermissions(terminal.MayExpand, terminal.MayConnect),
|
||||
"alpha2": terminal.AddPermissions(terminal.MayExpand, terminal.MayConnect),
|
||||
"fallback1": terminal.AddPermissions(terminal.MayExpand, terminal.MayConnect),
|
||||
}
|
||||
persistentZones = ExpandAndConnectZones
|
||||
|
||||
enableTestMode = abool.New()
|
||||
)
|
||||
|
||||
// EnableTestMode enables the test mode, leading the access module to only
|
||||
// register a test zone.
|
||||
// This should not be used to test the access module itself.
|
||||
func EnableTestMode() {
|
||||
enableTestMode.Set()
|
||||
}
|
||||
|
||||
// InitializeZones initialized the permission zones.
|
||||
// It initializes the test zones, if EnableTestMode was called before.
|
||||
// Must only be called once.
|
||||
func InitializeZones() error {
|
||||
// Check if we are testing.
|
||||
if enableTestMode.IsSet() {
|
||||
return initializeTestZone()
|
||||
}
|
||||
|
||||
// Special client zone config.
|
||||
var requestSignalHandler func(token.Handler)
|
||||
if conf.Client() {
|
||||
requestSignalHandler = shouldRequestTokensHandler
|
||||
}
|
||||
|
||||
// Register pblind1 as the first primary zone.
|
||||
ph, err := token.NewPBlindHandler(token.PBlindOptions{
|
||||
Zone: "pblind1",
|
||||
CurveName: "P-256",
|
||||
PublicKey: "eXoJXzXbM66UEsM2eVi9HwyBPLMfVnNrC7gNrsfMUJDs",
|
||||
UseSerials: true,
|
||||
BatchSize: 1000,
|
||||
RandomizeOrder: true,
|
||||
SignalShouldRequest: requestSignalHandler,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create pblind1 token handler: %w", err)
|
||||
}
|
||||
err = token.RegisterPBlindHandler(ph)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to register pblind1 token handler: %w", err)
|
||||
}
|
||||
|
||||
// Register fallback1 zone as fallback when the issuer is not available.
|
||||
sh, err := token.NewScrambleHandler(token.ScrambleOptions{
|
||||
Zone: "fallback1",
|
||||
Algorithm: lhash.BLAKE2b_256,
|
||||
InitialVerifiers: []string{"ZwkQoaAttVBMURzeLzNXokFBMAMUUwECfM1iHojcVKBmjk"},
|
||||
Fallback: true,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create fallback1 token handler: %w", err)
|
||||
}
|
||||
err = token.RegisterScrambleHandler(sh)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to register fallback1 token handler: %w", err)
|
||||
}
|
||||
|
||||
// Register alpha2 zone for transition phase.
|
||||
sh, err = token.NewScrambleHandler(token.ScrambleOptions{
|
||||
Zone: "alpha2",
|
||||
Algorithm: lhash.BLAKE2b_256,
|
||||
InitialVerifiers: []string{"ZwojEvXZmAv7SZdNe7m94Xzu7F9J8vULqKf7QYtoTpN2tH"},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create alpha2 token handler: %w", err)
|
||||
}
|
||||
err = token.RegisterScrambleHandler(sh)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to register alpha2 token handler: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initializeTestZone() error {
|
||||
// Safeguard checks if we should really enable the test zone.
|
||||
if !strings.HasSuffix(os.Args[0], ".test") {
|
||||
return errors.New("tried to enable test mode, but no test binary was detected")
|
||||
}
|
||||
if token.RegistrySize() > 0 {
|
||||
return fmt.Errorf("tried to enable test zone, but %d handlers are already registered", token.RegistrySize())
|
||||
}
|
||||
|
||||
// Reset zones.
|
||||
token.ResetRegistry()
|
||||
|
||||
// Set eligible zones.
|
||||
ExpandAndConnectZones = []string{"unittest"}
|
||||
zonePermissions = map[string]terminal.Permission{
|
||||
"unittest": terminal.AddPermissions(terminal.MayExpand, terminal.MayConnect),
|
||||
}
|
||||
|
||||
// Register unittest zone as for testing.
|
||||
sh, err := token.NewScrambleHandler(token.ScrambleOptions{
|
||||
Zone: "unittest",
|
||||
Algorithm: lhash.BLAKE2b_256,
|
||||
InitialTokens: []string{"6jFqLA93uSLL52utGKrvctG3ZfopSQ8WFqjsRK1c2Svt"},
|
||||
InitialVerifiers: []string{"ZwoEoL59sr81s7WnF2vydGzjeejE3u8CqVafig1NTQzUr7"},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create unittest token handler: %w", err)
|
||||
}
|
||||
err = token.RegisterScrambleHandler(sh)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to register unittest token handler: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func shouldRequestTokensHandler(_ token.Handler) {
|
||||
// accountUpdateTask is always set in client mode and when the module is online.
|
||||
// Check if it's set in case this gets executed in other circumstances.
|
||||
if accountUpdateTask == nil {
|
||||
log.Warningf("spn/access: trying to trigger account update, but the task is not available")
|
||||
return
|
||||
}
|
||||
|
||||
accountUpdateTask.StartASAP()
|
||||
}
|
||||
|
||||
// GetTokenAmount returns the amount of tokens for the given zones.
|
||||
func GetTokenAmount(zones []string) (regular, fallback int) {
|
||||
handlerLoop:
|
||||
for _, zone := range zones {
|
||||
// Get handler and check if it should be used.
|
||||
handler, ok := token.GetHandler(zone)
|
||||
if !ok {
|
||||
log.Warningf("spn/access: use of non-registered zone %q", zone)
|
||||
continue handlerLoop
|
||||
}
|
||||
|
||||
if handler.IsFallback() {
|
||||
fallback += handler.Amount()
|
||||
} else {
|
||||
regular += handler.Amount()
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ShouldRequest returns whether tokens should be requested for the given zones.
|
||||
func ShouldRequest(zones []string) (shouldRequest bool) {
|
||||
handlerLoop:
|
||||
for _, zone := range zones {
|
||||
// Get handler and check if it should be used.
|
||||
handler, ok := token.GetHandler(zone)
|
||||
if !ok {
|
||||
log.Warningf("spn/access: use of non-registered zone %q", zone)
|
||||
continue handlerLoop
|
||||
}
|
||||
|
||||
// Go through all handlers every time as this will be the case anyway most
|
||||
// of the time and will help us better catch zone misconfiguration.
|
||||
if handler.ShouldRequest() {
|
||||
shouldRequest = true
|
||||
}
|
||||
}
|
||||
|
||||
return shouldRequest
|
||||
}
|
||||
|
||||
// GetToken returns a token of one of the given zones.
|
||||
func GetToken(zones []string) (t *token.Token, err error) {
|
||||
handlerSelection:
|
||||
for _, zone := range zones {
|
||||
// Get handler and check if it should be used.
|
||||
handler, ok := token.GetHandler(zone)
|
||||
switch {
|
||||
case !ok:
|
||||
log.Warningf("spn/access: use of non-registered zone %q", zone)
|
||||
continue handlerSelection
|
||||
case handler.IsFallback() && !TokenIssuerIsFailing():
|
||||
// Skip fallback zone if everything works.
|
||||
continue handlerSelection
|
||||
}
|
||||
|
||||
// Get token from handler.
|
||||
t, err = token.GetToken(zone)
|
||||
if err == nil {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Return existing error, if exists.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, token.ErrEmpty
|
||||
}
|
||||
|
||||
// VerifyRawToken verifies a raw token.
|
||||
func VerifyRawToken(data []byte) (granted terminal.Permission, err error) {
|
||||
t, err := token.ParseRawToken(data)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to parse token: %w", err)
|
||||
}
|
||||
|
||||
return VerifyToken(t)
|
||||
}
|
||||
|
||||
// VerifyToken verifies a token.
|
||||
func VerifyToken(t *token.Token) (granted terminal.Permission, err error) {
|
||||
handler, ok := token.GetHandler(t.Zone)
|
||||
if !ok {
|
||||
return terminal.NoPermission, token.ErrZoneUnknown
|
||||
}
|
||||
|
||||
// Check if the token is a fallback token.
|
||||
if handler.IsFallback() && !healthCheck() {
|
||||
return terminal.NoPermission, ErrFallbackNotAvailable
|
||||
}
|
||||
|
||||
// Verify token.
|
||||
err = handler.Verify(t)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to verify token: %w", err)
|
||||
}
|
||||
|
||||
// Return permission of zone.
|
||||
granted, ok = zonePermissions[t.Zone]
|
||||
if !ok {
|
||||
return terminal.NoPermission, nil
|
||||
}
|
||||
return granted, nil
|
||||
}
|
||||
Reference in New Issue
Block a user