Merge pull request #938 from safing/fix/verdict-and-profile-issues

Fix verdict and profile issues
This commit is contained in:
Daniel Hovie
2022-10-13 15:38:22 +02:00
committed by GitHub
12 changed files with 154 additions and 160 deletions

View File

@@ -25,9 +25,10 @@ import (
func registerAPIEndpoints() error { func registerAPIEndpoints() error {
if err := api.RegisterEndpoint(api.Endpoint{ if err := api.RegisterEndpoint(api.Endpoint{
Path: "core/shutdown", Path: "core/shutdown",
Write: api.PermitSelf, Write: api.PermitSelf,
BelongsTo: module, // Do NOT register as belonging to the module, so that the API is available
// when something fails during starting of this module or a dependency.
ActionFunc: shutdown, ActionFunc: shutdown,
Name: "Shut Down Portmaster", Name: "Shut Down Portmaster",
Description: "Shut down the Portmaster Core Service and all UI components.", Description: "Shut down the Portmaster Core Service and all UI components.",
@@ -36,9 +37,10 @@ func registerAPIEndpoints() error {
} }
if err := api.RegisterEndpoint(api.Endpoint{ if err := api.RegisterEndpoint(api.Endpoint{
Path: "core/restart", Path: "core/restart",
Write: api.PermitAdmin, Write: api.PermitAdmin,
BelongsTo: module, // Do NOT register as belonging to the module, so that the API is available
// when something fails during starting of this module or a dependency.
ActionFunc: restart, ActionFunc: restart,
Name: "Restart Portmaster", Name: "Restart Portmaster",
Description: "Restart the Portmaster Core Service.", Description: "Restart the Portmaster Core Service.",

View File

@@ -112,7 +112,7 @@ func interceptionPrep() error {
func resetAllConnectionVerdicts() { func resetAllConnectionVerdicts() {
// Resetting will force all the connection to be evaluated by the firewall again // Resetting will force all the connection to be evaluated by the firewall again
// this will set new verdicts if configuration was update or spn has been disabled or enabled. // this will set new verdicts if configuration was update or spn has been disabled or enabled.
log.Info("interception: marking all connections for re-evaluation") log.Info("interception: re-evaluating all connections")
// Create tracing context. // Create tracing context.
ctx, tracer := log.AddTracer(context.Background()) ctx, tracer := log.AddTracer(context.Background())
@@ -137,7 +137,7 @@ func resetAllConnectionVerdicts() {
previousVerdict := conn.Verdict.Firewall previousVerdict := conn.Verdict.Firewall
// Apply privacy filter and check tunneling. // Apply privacy filter and check tunneling.
filterConnection(ctx, conn, nil) FilterConnection(ctx, conn, nil, true, true)
// Stop existing SPN tunnel if not needed anymore. // Stop existing SPN tunnel if not needed anymore.
if conn.Verdict.Active != network.VerdictRerouteToTunnel && conn.TunnelContext != nil { if conn.Verdict.Active != network.VerdictRerouteToTunnel && conn.TunnelContext != nil {
@@ -157,7 +157,7 @@ func resetAllConnectionVerdicts() {
} }
}() }()
} }
tracer.Infof("profile: changed verdict on %d connections", changedVerdicts) tracer.Infof("filter: changed verdict on %d connections", changedVerdicts)
tracer.Submit() tracer.Submit()
err := interception.ResetVerdictOfAllConnections() err := interception.ResetVerdictOfAllConnections()
@@ -437,14 +437,17 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
} }
func initialHandler(conn *network.Connection, pkt packet.Packet) { func initialHandler(conn *network.Connection, pkt packet.Packet) {
log.Tracer(pkt.Ctx()).Trace("filter: handing over to connection-based handler") filterConnection := true
log.Tracer(pkt.Ctx()).Trace("filter: handing over to connection-based handler")
// Check for special (internal) connection cases. // Check for special (internal) connection cases.
switch { switch {
case !conn.Inbound && localPortIsPreAuthenticated(conn.Entity.Protocol, conn.LocalPort): case !conn.Inbound && localPortIsPreAuthenticated(conn.Entity.Protocol, conn.LocalPort):
// Approve connection. // Approve connection.
conn.Accept("connection by Portmaster", noReasonOptionKey) conn.Accept("connection by Portmaster", noReasonOptionKey)
conn.Internal = true conn.Internal = true
filterConnection = false
log.Tracer(pkt.Ctx()).Infof("filter: granting own pre-authenticated connection %s", conn)
// Redirect outbound DNS packets if enabled, // Redirect outbound DNS packets if enabled,
case dnsQueryInterception() && case dnsQueryInterception() &&
@@ -461,9 +464,9 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
conn.Entity.IPScope == netutils.LocalMulticast): conn.Entity.IPScope == netutils.LocalMulticast):
// Reroute rogue dns queries back to Portmaster. // Reroute rogue dns queries back to Portmaster.
conn.SetVerdictDirectly(network.VerdictRerouteToNameserver) conn.SetVerdict(network.VerdictRerouteToNameserver, "redirecting rogue dns query", "", nil)
conn.Reason.Msg = "redirecting rogue dns query"
conn.Internal = true conn.Internal = true
log.Tracer(pkt.Ctx()).Infof("filter: redirecting dns query %s to Portmaster", conn)
// End directly, as no other processing is necessary. // End directly, as no other processing is necessary.
conn.StopFirewallHandler() conn.StopFirewallHandler()
finalizeVerdict(conn) finalizeVerdict(conn)
@@ -472,7 +475,7 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
} }
// Apply privacy filter and check tunneling. // Apply privacy filter and check tunneling.
filterConnection(pkt.Ctx(), conn, pkt) FilterConnection(pkt.Ctx(), conn, pkt, filterConnection, true)
// Decide how to continue handling connection. // Decide how to continue handling connection.
switch { switch {
@@ -486,12 +489,15 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
} }
} }
func filterConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet) { // FilterConnection runs all the filtering (and tunneling) procedures.
if filterEnabled() { func FilterConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet, checkFilter, checkTunnel bool) {
log.Tracer(ctx).Trace("filter: starting decision process") if checkFilter {
DecideOnConnection(ctx, conn, pkt) if filterEnabled() {
} else { log.Tracer(ctx).Trace("filter: starting decision process")
conn.Accept("privacy filter disabled", noReasonOptionKey) decideOnConnection(ctx, conn, pkt)
} else {
conn.Accept("privacy filter disabled", noReasonOptionKey)
}
} }
// TODO: Enable inspection framework again. // TODO: Enable inspection framework again.
@@ -511,7 +517,9 @@ func filterConnection(ctx context.Context, conn *network.Connection, pkt packet.
} }
// Check if connection should be tunneled. // Check if connection should be tunneled.
checkTunneling(ctx, conn) if checkTunnel {
checkTunneling(ctx, conn)
}
// Handle verdict records and transitions. // Handle verdict records and transitions.
finalizeVerdict(conn) finalizeVerdict(conn)

View File

@@ -58,9 +58,9 @@ var defaultDeciders = []deciderFn{
checkAutoPermitRelated, checkAutoPermitRelated,
} }
// DecideOnConnection makes a decision about a connection. // decideOnConnection makes a decision about a connection.
// When called, the connection and profile is already locked. // When called, the connection and profile is already locked.
func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet) { func decideOnConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet) {
// Check if we have a process and profile. // Check if we have a process and profile.
layeredProfile := conn.Process().Profile() layeredProfile := conn.Process().Profile()
if layeredProfile == nil { if layeredProfile == nil {
@@ -141,15 +141,34 @@ func runDeciders(ctx context.Context, selectedDeciders []deciderFn, conn *networ
// checkPortmasterConnection allows all connection that originate from // checkPortmasterConnection allows all connection that originate from
// portmaster itself. // portmaster itself.
func checkPortmasterConnection(ctx context.Context, conn *network.Connection, _ *profile.LayeredProfile, _ packet.Packet) bool { func checkPortmasterConnection(ctx context.Context, conn *network.Connection, _ *profile.LayeredProfile, _ packet.Packet) bool {
// Grant own outgoing connections. // Grant own outgoing or local connections.
if conn.Process().Pid == ownPID && !conn.Inbound {
log.Tracer(ctx).Infof("filter: granting own connection %s", conn) // Blocking our own connections can lead to a very literal deadlock.
conn.Accept("connection by Portmaster", noReasonOptionKey) // This can currently happen, as fast-tracked connections are also
conn.Internal = true // reset in the OS integration and might show up in the connection
return true // handling if a packet in the other direction hits the firewall first.
// Ignore other processes.
if conn.Process().Pid != ownPID {
return false
} }
return false // Ignore inbound connection if non-local.
if conn.Inbound {
myIP, err := netenv.IsMyIP(conn.Entity.IP)
if err != nil {
log.Tracer(ctx).Debugf("filter: failed to check if %s is own IP for granting own connection: %s", conn.Entity.IP, err)
return false
}
if !myIP {
return false
}
}
log.Tracer(ctx).Infof("filter: granting own connection %s", conn)
conn.Accept("connection by Portmaster", noReasonOptionKey)
conn.Internal = true
return true
} }
// checkSelfCommunication checks if the process is communicating with itself. // checkSelfCommunication checks if the process is communicating with itself.

View File

@@ -222,7 +222,7 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
}() }()
// Check request with the privacy filter before resolving. // Check request with the privacy filter before resolving.
firewall.DecideOnConnection(ctx, conn, nil) firewall.FilterConnection(ctx, conn, nil, true, false)
// Check if there is a responder from the firewall. // Check if there is a responder from the firewall.
// In special cases, the firewall might want to respond the query itself. // In special cases, the firewall might want to respond the query itself.

View File

@@ -333,7 +333,7 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
proc, inbound, err := process.GetProcessByConnection(pkt.Ctx(), pkt.Info()) proc, inbound, err := process.GetProcessByConnection(pkt.Ctx(), pkt.Info())
if err != nil { if err != nil {
log.Tracer(pkt.Ctx()).Debugf("network: failed to find process of packet %s: %s", pkt, err) log.Tracer(pkt.Ctx()).Debugf("network: failed to find process of packet %s: %s", pkt, err)
if inbound { if inbound && !netutils.ClassifyIP(pkt.Info().Dst).IsLocalhost() {
proc = process.GetUnsolicitedProcess(pkt.Ctx()) proc = process.GetUnsolicitedProcess(pkt.Ctx())
} else { } else {
proc = process.GetUnidentifiedProcess(pkt.Ctx()) proc = process.GetUnidentifiedProcess(pkt.Ctx())

View File

@@ -27,18 +27,10 @@ func (p *Process) GetProfile(ctx context.Context) (changed bool, err error) {
// If not, continue with loading the profile. // If not, continue with loading the profile.
log.Tracer(ctx).Trace("process: loading profile") log.Tracer(ctx).Trace("process: loading profile")
// Check if there is a special profile for this process. // Get special or regular profile.
localProfile, err := p.loadSpecialProfile(ctx) localProfile, err := profile.GetLocalProfile(p.getSpecialProfileID(), p.MatchingData(), p.CreateProfileCallback)
if err != nil { if err != nil {
return false, fmt.Errorf("failed to load special profile: %w", err) return false, fmt.Errorf("failed to find profile: %w", err)
}
// Otherwise, find a regular profile for the process.
if localProfile == nil {
localProfile, err = profile.GetLocalProfile("", p.MatchingData(), p.CreateProfileCallback)
if err != nil {
return false, fmt.Errorf("failed to find profile: %w", err)
}
} }
// Assign profile to process. // Assign profile to process.
@@ -48,10 +40,9 @@ func (p *Process) GetProfile(ctx context.Context) (changed bool, err error) {
return true, nil return true, nil
} }
// loadSpecialProfile attempts to load a special profile. // getSpecialProfileID returns the special profile ID for the process, if any.
func (p *Process) loadSpecialProfile(_ context.Context) (*profile.Profile, error) { func (p *Process) getSpecialProfileID() (specialProfileID string) {
// Check if we need a special profile. // Check if we need a special profile.
var specialProfileID string
switch p.Pid { switch p.Pid {
case UnidentifiedProcessID: case UnidentifiedProcessID:
specialProfileID = profile.UnidentifiedProfileID specialProfileID = profile.UnidentifiedProfileID
@@ -103,11 +94,5 @@ func (p *Process) loadSpecialProfile(_ context.Context) (*profile.Profile, error
} }
} }
// Check if a special profile should be applied. return specialProfileID
if specialProfileID == "" {
return nil, nil
}
// Return special profile.
return profile.GetSpecialProfile(specialProfileID, p.Path)
} }

View File

@@ -44,11 +44,11 @@ func updateGlobalConfigProfile(ctx context.Context, task *modules.Task) error {
action := cfgOptionDefaultAction() action := cfgOptionDefaultAction()
switch action { switch action {
case "permit": case DefaultActionPermitValue:
cfgDefaultAction = DefaultActionPermit cfgDefaultAction = DefaultActionPermit
case "ask": case DefaultActionAskValue:
cfgDefaultAction = DefaultActionAsk cfgDefaultAction = DefaultActionAsk
case "block": case DefaultActionBlockValue:
cfgDefaultAction = DefaultActionBlock cfgDefaultAction = DefaultActionBlock
default: default:
// TODO: module error? // TODO: module error?

View File

@@ -23,6 +23,10 @@ var (
cfgOptionDefaultAction config.StringOption cfgOptionDefaultAction config.StringOption
cfgOptionDefaultActionOrder = 1 cfgOptionDefaultActionOrder = 1
DefaultActionPermitValue = "permit"
DefaultActionBlockValue = "block"
DefaultActionAskValue = "ask"
// Setting "Prompt Desktop Notifications" at order 2. // Setting "Prompt Desktop Notifications" at order 2.
// Setting "Prompt Timeout" at order 3. // Setting "Prompt Timeout" at order 3.
@@ -182,7 +186,7 @@ func registerConfiguration() error { //nolint:maintidx
Key: CfgOptionDefaultActionKey, Key: CfgOptionDefaultActionKey,
Description: `The default network action is applied when nothing else allows or blocks an outgoing connection. Incoming connections are always blocked by default.`, Description: `The default network action is applied when nothing else allows or blocks an outgoing connection. Incoming connections are always blocked by default.`,
OptType: config.OptTypeString, OptType: config.OptTypeString,
DefaultValue: "permit", DefaultValue: DefaultActionPermitValue,
Annotations: config.Annotations{ Annotations: config.Annotations{
config.DisplayHintAnnotation: config.DisplayHintOneOf, config.DisplayHintAnnotation: config.DisplayHintOneOf,
config.DisplayOrderAnnotation: cfgOptionDefaultActionOrder, config.DisplayOrderAnnotation: cfgOptionDefaultActionOrder,
@@ -191,17 +195,17 @@ func registerConfiguration() error { //nolint:maintidx
PossibleValues: []config.PossibleValue{ PossibleValues: []config.PossibleValue{
{ {
Name: "Allow", Name: "Allow",
Value: "permit", Value: DefaultActionPermitValue,
Description: "Allow all connections", Description: "Allow all connections",
}, },
{ {
Name: "Block", Name: "Block",
Value: "block", Value: DefaultActionBlockValue,
Description: "Block all connections", Description: "Block all connections",
}, },
{ {
Name: "Prompt", Name: "Prompt",
Value: "ask", Value: DefaultActionAskValue,
Description: "Prompt for decisions", Description: "Prompt for decisions",
}, },
}, },
@@ -209,7 +213,7 @@ func registerConfiguration() error { //nolint:maintidx
if err != nil { if err != nil {
return err return err
} }
cfgOptionDefaultAction = config.Concurrent.GetAsString(CfgOptionDefaultActionKey, "permit") cfgOptionDefaultAction = config.Concurrent.GetAsString(CfgOptionDefaultActionKey, DefaultActionPermitValue)
cfgStringOptions[CfgOptionDefaultActionKey] = cfgOptionDefaultAction cfgStringOptions[CfgOptionDefaultActionKey] = cfgOptionDefaultAction
// Disable Auto Permit // Disable Auto Permit

View File

@@ -7,6 +7,7 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/safing/portbase/database"
"github.com/safing/portbase/database/query" "github.com/safing/portbase/database/query"
"github.com/safing/portbase/database/record" "github.com/safing/portbase/database/record"
"github.com/safing/portbase/log" "github.com/safing/portbase/log"
@@ -50,6 +51,7 @@ func GetLocalProfile(id string, md MatchingData, createProfileCallback func() *P
// In some cases, we might need to get a profile directly, without matching data. // In some cases, we might need to get a profile directly, without matching data.
// This could lead to inconsistent data - use with caution. // This could lead to inconsistent data - use with caution.
// Example: Saving prompt results to profile should always be to the same ID!
if md == nil { if md == nil {
if id == "" { if id == "" {
return nil, errors.New("cannot get local profiles without ID and matching data") return nil, errors.New("cannot get local profiles without ID and matching data")
@@ -61,6 +63,26 @@ func GetLocalProfile(id string, md MatchingData, createProfileCallback func() *P
} }
} }
// Check if we are requesting a special profile.
var created, special bool
if id != "" && isSpecialProfileID(id) {
special = true
// Get special profile from DB.
if profile == nil {
profile, err = getProfile(makeScopedID(SourceLocal, id))
if err != nil && !errors.Is(err, database.ErrNotFound) {
log.Warningf("profile: failed to get special profile %s: %s", id, err)
}
}
// Create profile if not found or if it needs a reset.
if profile == nil || specialProfileNeedsReset(profile) {
profile = createSpecialProfile(id, md.Path())
created = true
}
}
// If we don't have a profile yet, find profile based on matching data. // If we don't have a profile yet, find profile based on matching data.
if profile == nil { if profile == nil {
profile, err = findProfile(SourceLocal, md) profile, err = findProfile(SourceLocal, md)
@@ -70,7 +92,6 @@ func GetLocalProfile(id string, md MatchingData, createProfileCallback func() *P
} }
// If we still don't have a profile, create a new one. // If we still don't have a profile, create a new one.
var created bool
if profile == nil { if profile == nil {
created = true created = true
@@ -105,7 +126,12 @@ func GetLocalProfile(id string, md MatchingData, createProfileCallback func() *P
// Initialize and update profile. // Initialize and update profile.
// Update metadata. // Update metadata.
changed := profile.updateMetadata(md.Path()) var changed bool
if special {
changed = updateSpecialProfileMetadata(profile, md.Path())
} else {
changed = profile.updateMetadata(md.Path())
}
// Save if created or changed. // Save if created or changed.
if created || changed { if created || changed {
@@ -117,7 +143,7 @@ func GetLocalProfile(id string, md MatchingData, createProfileCallback func() *P
} }
// Trigger further metadata fetching from system if profile was created. // Trigger further metadata fetching from system if profile was created.
if created && profile.UsePresentationPath { if created && profile.UsePresentationPath && !special {
module.StartWorker("get profile metadata", profile.updateMetadataFromSystem) module.StartWorker("get profile metadata", profile.updateMetadataFromSystem)
} }

View File

@@ -2,7 +2,6 @@ package profile
import ( import (
"context" "context"
"fmt"
"github.com/hashicorp/go-version" "github.com/hashicorp/go-version"
@@ -62,7 +61,8 @@ func migrateLinkedPath(ctx context.Context, _, to *version.Version, db *database
// Get iterator over all profiles. // Get iterator over all profiles.
it, err := db.Query(query.New(profilesDBPath)) it, err := db.Query(query.New(profilesDBPath))
if err != nil { if err != nil {
return fmt.Errorf("failed to query profiles: %w", err) log.Tracer(ctx).Errorf("profile: failed to migrate from linked path: failed to start query: %s", err)
return nil
} }
// Migrate all profiles. // Migrate all profiles.
@@ -91,8 +91,8 @@ func migrateLinkedPath(ctx context.Context, _, to *version.Version, db *database
} }
// Check if there was an error while iterating. // Check if there was an error while iterating.
if it.Err() != nil { if err := it.Err(); err != nil {
return fmt.Errorf("profiles: failed to iterate over profiles for migration: %w", err) log.Tracer(ctx).Errorf("profile: failed to migrate from linked path: failed to iterate over profiles for migration: %s", err)
} }
return nil return nil

View File

@@ -175,11 +175,11 @@ func (profile *Profile) parseConfig() error {
profile.defaultAction = DefaultActionNotSet profile.defaultAction = DefaultActionNotSet
if ok { if ok {
switch action { switch action {
case "permit": case DefaultActionPermitValue:
profile.defaultAction = DefaultActionPermit profile.defaultAction = DefaultActionPermit
case "ask": case DefaultActionAskValue:
profile.defaultAction = DefaultActionAsk profile.defaultAction = DefaultActionAsk
case "block": case DefaultActionBlockValue:
profile.defaultAction = DefaultActionBlock profile.defaultAction = DefaultActionBlock
default: default:
lastErr = fmt.Errorf(`default action "%s" invalid`, action) lastErr = fmt.Errorf(`default action "%s" invalid`, action)

View File

@@ -1,10 +1,8 @@
package profile package profile
import ( import (
"errors"
"time" "time"
"github.com/safing/portbase/database"
"github.com/safing/portbase/log" "github.com/safing/portbase/log"
"github.com/safing/portmaster/status" "github.com/safing/portmaster/status"
) )
@@ -13,7 +11,7 @@ const (
// UnidentifiedProfileID is the profile ID used for unidentified processes. // UnidentifiedProfileID is the profile ID used for unidentified processes.
UnidentifiedProfileID = "_unidentified" UnidentifiedProfileID = "_unidentified"
// UnidentifiedProfileName is the name used for unidentified processes. // UnidentifiedProfileName is the name used for unidentified processes.
UnidentifiedProfileName = "Unidentified App" UnidentifiedProfileName = "Other Connections"
// UnidentifiedProfileDescription is the description used for unidentified processes. // UnidentifiedProfileDescription is the description used for unidentified processes.
UnidentifiedProfileDescription = `Connections that could not be attributed to a specific app. UnidentifiedProfileDescription = `Connections that could not be attributed to a specific app.
@@ -77,91 +75,19 @@ If you think you might have messed up the settings of the System DNS Client, jus
PortmasterNotifierProfileDescription = `This is the Portmaster UI Tray Notifier.` PortmasterNotifierProfileDescription = `This is the Portmaster UI Tray Notifier.`
) )
// GetSpecialProfile fetches a special profile. This function ensures that the loaded profile func isSpecialProfileID(id string) bool {
// is shared among all callers. Always provide all available data points. switch id {
func GetSpecialProfile(id string, path string) ( //nolint:gocognit case UnidentifiedProfileID,
profile *Profile, UnsolicitedProfileID,
err error, SystemProfileID,
) { SystemResolverProfileID,
// Check if we have an ID. PortmasterProfileID,
if id == "" { PortmasterAppProfileID,
return nil, errors.New("cannot get special profile without ID") PortmasterNotifierProfileID:
} return true
scopedID := makeScopedID(SourceLocal, id)
// Globally lock getting a profile.
// This does not happen too often, and it ensures we really have integrity
// and no race conditions.
getProfileLock.Lock()
defer getProfileLock.Unlock()
// Check if there already is an active profile.
var previousVersion *Profile
profile = getActiveProfile(scopedID)
if profile != nil {
// Mark active and return if not outdated.
if profile.outdated.IsNotSet() {
profile.MarkStillActive()
return profile, nil
}
// If outdated, get from database.
previousVersion = profile
}
// Get special profile from DB and check if it needs a reset.
var created bool
profile, err = getProfile(scopedID)
switch {
case err == nil:
// Reset profile if needed.
if specialProfileNeedsReset(profile) {
profile = createSpecialProfile(id, path)
created = true
}
case !errors.Is(err, database.ErrNotFound):
// Warn when fetching from DB fails, and create new profile as fallback.
log.Warningf("profile: failed to get special profile %s: %s", id, err)
fallthrough
default: default:
// Create new profile if it does not exist (or failed to load). return false
profile = createSpecialProfile(id, path)
created = true
} }
// Check if creating the special profile was successful.
if profile == nil {
return nil, errors.New("given ID is not a special profile ID")
}
// Update metadata
changed := updateSpecialProfileMetadata(profile, path)
// Save if created or changed.
if created || changed {
err := profile.Save()
if err != nil {
log.Warningf("profile: failed to save special profile %s: %s", scopedID, err)
}
}
// Prepare profile for first use.
// If we are refetching, assign the layered profile from the previous version.
// The internal references will be updated when the layered profile checks for updates.
if previousVersion != nil && previousVersion.layeredProfile != nil {
profile.layeredProfile = previousVersion.layeredProfile
}
// Profiles must have a layered profile, create a new one if it
// does not yet exist.
if profile.layeredProfile == nil {
profile.layeredProfile = NewLayeredProfile(profile)
}
// Add the profile to the currently active profiles.
addActiveProfile(profile)
return profile, nil
} }
func updateSpecialProfileMetadata(profile *Profile, binaryPath string) (changed bool) { func updateSpecialProfileMetadata(profile *Profile, binaryPath string) (changed bool) {
@@ -248,7 +174,7 @@ func createSpecialProfile(profileID string, path string) *Profile {
// Resolved domain from the system resolver are checked again when // Resolved domain from the system resolver are checked again when
// attributed to a connection of a regular process. Otherwise, users // attributed to a connection of a regular process. Otherwise, users
// would see two connection prompts for the same domain. // would see two connection prompts for the same domain.
CfgOptionDefaultActionKey: "permit", CfgOptionDefaultActionKey: DefaultActionPermitValue,
// Explicitly allow incoming connections. // Explicitly allow incoming connections.
CfgOptionBlockInboundKey: status.SecurityLevelOff, CfgOptionBlockInboundKey: status.SecurityLevelOff,
// Explicitly allow localhost and answers to multicast protocols that // Explicitly allow localhost and answers to multicast protocols that
@@ -276,7 +202,29 @@ func createSpecialProfile(profileID string, path string) *Profile {
ID: PortmasterProfileID, ID: PortmasterProfileID,
Source: SourceLocal, Source: SourceLocal,
PresentationPath: path, PresentationPath: path,
Internal: true, Config: map[string]interface{}{
// In case anything slips through the internal self-allow, be sure to
// allow everything explicitly.
// Blocking connections here can lead to a very literal deadlock.
// This can currently happen, as fast-tracked connections are also
// reset in the OS integration and might show up in the connection
// handling if a packet in the other direction hits the firewall first.
CfgOptionDefaultActionKey: DefaultActionPermitValue,
CfgOptionBlockScopeInternetKey: status.SecurityLevelOff,
CfgOptionBlockScopeLANKey: status.SecurityLevelOff,
CfgOptionBlockScopeLocalKey: status.SecurityLevelOff,
CfgOptionBlockP2PKey: status.SecurityLevelOff,
CfgOptionBlockInboundKey: status.SecurityLevelOff,
CfgOptionEndpointsKey: []string{
"+ *",
},
CfgOptionServiceEndpointsKey: []string{
"+ Localhost",
"+ LAN",
"- *",
},
},
Internal: true,
}) })
case PortmasterAppProfileID: case PortmasterAppProfileID:
@@ -285,7 +233,7 @@ func createSpecialProfile(profileID string, path string) *Profile {
Source: SourceLocal, Source: SourceLocal,
PresentationPath: path, PresentationPath: path,
Config: map[string]interface{}{ Config: map[string]interface{}{
CfgOptionDefaultActionKey: "block", CfgOptionDefaultActionKey: DefaultActionBlockValue,
CfgOptionEndpointsKey: []string{ CfgOptionEndpointsKey: []string{
"+ Localhost", "+ Localhost",
"+ .safing.io", "+ .safing.io",
@@ -300,7 +248,7 @@ func createSpecialProfile(profileID string, path string) *Profile {
Source: SourceLocal, Source: SourceLocal,
PresentationPath: path, PresentationPath: path,
Config: map[string]interface{}{ Config: map[string]interface{}{
CfgOptionDefaultActionKey: "block", CfgOptionDefaultActionKey: DefaultActionBlockValue,
CfgOptionEndpointsKey: []string{ CfgOptionEndpointsKey: []string{
"+ Localhost", "+ Localhost",
}, },
@@ -334,6 +282,8 @@ func specialProfileNeedsReset(profile *Profile) bool {
switch profile.ID { switch profile.ID {
case SystemResolverProfileID: case SystemResolverProfileID:
return canBeUpgraded(profile, "21.10.2022") return canBeUpgraded(profile, "21.10.2022")
case PortmasterProfileID:
return canBeUpgraded(profile, "21.10.2022")
case PortmasterAppProfileID: case PortmasterAppProfileID:
return canBeUpgraded(profile, "8.9.2021") return canBeUpgraded(profile, "8.9.2021")
default: default: