diff --git a/network/connection.go b/network/connection.go index 99078c87..f8b130e9 100644 --- a/network/connection.go +++ b/network/connection.go @@ -228,6 +228,12 @@ func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []stri Started: timestamp, Ended: timestamp, } + + // Inherit internal status of profile. + if localProfile := proc.Profile().LocalProfile(); localProfile != nil { + dnsConn.Internal = localProfile.Internal + } + return dnsConn } @@ -238,7 +244,7 @@ func NewConnectionFromExternalDNSRequest(ctx context.Context, fqdn string, cname } timestamp := time.Now().Unix() - return &Connection{ + dnsConn := &Connection{ Scope: fqdn, Entity: &intel.Entity{ Domain: fqdn, @@ -248,7 +254,14 @@ func NewConnectionFromExternalDNSRequest(ctx context.Context, fqdn string, cname ProcessContext: getProcessContext(ctx, remoteHost), Started: timestamp, Ended: timestamp, - }, nil + } + + // Inherit internal status of profile. + if localProfile := remoteHost.Profile().LocalProfile(); localProfile != nil { + dnsConn.Internal = localProfile.Internal + } + + return dnsConn, nil } // NewConnectionFromFirstPacket returns a new connection based on the given packet. @@ -335,7 +348,8 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection { } } - return &Connection{ + // Create new connection object. + newConn := &Connection{ ID: pkt.GetConnectionID(), Scope: scope, IPVersion: pkt.Info().Version, @@ -352,6 +366,13 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection { Started: time.Now().Unix(), ProfileRevisionCounter: proc.Profile().RevisionCnt(), } + + // Inherit internal status of profile. + if localProfile := proc.Profile().LocalProfile(); localProfile != nil { + newConn.Internal = localProfile.Internal + } + + return newConn } // GetConnection fetches a Connection from the database. diff --git a/process/module.go b/process/module.go index bfed029e..4134d755 100644 --- a/process/module.go +++ b/process/module.go @@ -1,17 +1,32 @@ package process import ( + "os" + + "github.com/safing/portbase/log" "github.com/safing/portbase/modules" + "github.com/safing/portmaster/updates" ) var ( - module *modules.Module + module *modules.Module + updatesPath string ) func init() { - module = modules.Register("processes", prep, nil, nil, "profiles") + module = modules.Register("processes", prep, start, nil, "profiles", "updates") } func prep() error { return registerConfiguration() } + +func start() error { + updatesPath = updates.RootPath() + if updatesPath != "" { + updatesPath += string(os.PathSeparator) + } + log.Warningf("process: using updates path %s", updatesPath) + + return nil +} diff --git a/process/profile.go b/process/profile.go index 3294a281..792c11f8 100644 --- a/process/profile.go +++ b/process/profile.go @@ -3,6 +3,7 @@ package process import ( "context" "os" + "strings" "github.com/safing/portbase/log" "github.com/safing/portmaster/profile" @@ -38,6 +39,21 @@ func (p *Process) GetProfile(ctx context.Context) (changed bool, err error) { profileID = profile.SystemProfileID case ownPID: profileID = profile.PortmasterProfileID + default: + // Check if this is another Portmaster component. + if updatesPath != "" && strings.HasPrefix(p.Path, updatesPath) { + switch { + case strings.Contains(p.Path, "portmaster-app"): + profileID = profile.PortmasterAppProfileID + case strings.Contains(p.Path, "portmaster-notifier"): + profileID = profile.PortmasterNotifierProfileID + default: + // Unexpected binary from within the Portmaster updates directpry. + log.Warningf("process: unexpected binary in the updates directory: %s", p.Path) + // TODO: Assign a fully restricted profile in the future when we are + // sure that we won't kill any of our own things. + } + } } // Get the (linked) local profile. @@ -63,7 +79,7 @@ func (p *Process) UpdateProfileMetadata() { } // Update metadata of profile. - metadataUpdated := localProfile.UpdateMetadata(p.Name, p.Path) + metadataUpdated := localProfile.UpdateMetadata(p.Path) // Mark profile as used. profileChanged := localProfile.MarkUsed() diff --git a/profile/config-update.go b/profile/config-update.go index 22b76c60..c403112a 100644 --- a/profile/config-update.go +++ b/profile/config-update.go @@ -76,7 +76,7 @@ func updateGlobalConfigProfile(ctx context.Context, task *modules.Task) error { } // build global profile for reference - profile := New(SourceSpecial, "global-config", "") + profile := New(SourceSpecial, "global-config", "", nil) profile.Name = "Global Configuration" profile.Internal = true diff --git a/profile/get.go b/profile/get.go index 7e1e88be..b1f84ad8 100644 --- a/profile/get.go +++ b/profile/get.go @@ -2,30 +2,16 @@ package profile import ( "errors" - "os" "strings" "github.com/safing/portbase/database" - "github.com/safing/portbase/dataroot" - "github.com/safing/portbase/database/query" "github.com/safing/portbase/database/record" "github.com/safing/portbase/log" "golang.org/x/sync/singleflight" ) -const ( - // UnidentifiedProfileID is the profile ID used for unidentified processes. - UnidentifiedProfileID = "_unidentified" - - // SystemProfileID is the profile ID used for the system/kernel. - SystemProfileID = "_system" - - // SystemProfileID is the profile ID used for the Portmaster itself. - PortmasterProfileID = "_portmaster" -) - var getProfileSingleInflight singleflight.Group // GetProfile fetches a profile. This function ensures that the loaded profile @@ -69,15 +55,8 @@ func GetProfile(source profileSource, id, linkedPath string) ( //nolint:gocognit // If we cannot find a profile, check if the request is for a special // profile we can create. if errors.Is(err, database.ErrNotFound) { - switch id { - case UnidentifiedProfileID: - profile = New(SourceLocal, UnidentifiedProfileID, linkedPath) - err = nil - case SystemProfileID: - profile = New(SourceLocal, SystemProfileID, linkedPath) - err = nil - case PortmasterProfileID: - profile = New(SourceLocal, PortmasterProfileID, linkedPath) + profile = getSpecialProfile(id, linkedPath) + if profile != nil { err = nil } } @@ -173,11 +152,11 @@ func findProfile(linkedPath string) (profile *Profile, err error) { } // If there was no profile in the database, create a new one, and return it. - profile = New(SourceLocal, "", linkedPath) + profile = New(SourceLocal, "", linkedPath, nil) // Check if the profile should be marked as internal. // This is the case whenever the binary resides within the data root dir. - if strings.HasPrefix(linkedPath, dataroot.Root().Dir+string(os.PathSeparator)) { + if updatesPath != "" && strings.HasPrefix(linkedPath, updatesPath) { profile.Internal = true } diff --git a/profile/module.go b/profile/module.go index c42db691..7a62aa1a 100644 --- a/profile/module.go +++ b/profile/module.go @@ -1,17 +1,19 @@ package profile import ( - "github.com/safing/portbase/log" + "os" + "github.com/safing/portbase/log" "github.com/safing/portbase/modules" + "github.com/safing/portmaster/updates" // module dependencies _ "github.com/safing/portmaster/core/base" - _ "github.com/safing/portmaster/updates" // dependency of semi-dependency filterlists ) var ( - module *modules.Module + module *modules.Module + updatesPath string ) func init() { @@ -33,6 +35,12 @@ func prep() error { } func start() error { + updatesPath = updates.RootPath() + if updatesPath != "" { + updatesPath += string(os.PathSeparator) + } + log.Warningf("profile: using updates path %s", updatesPath) + err := registerValidationDBHook() if err != nil { return err diff --git a/profile/profile.go b/profile/profile.go index 46668249..aaf69304 100644 --- a/profile/profile.go +++ b/profile/profile.go @@ -193,13 +193,25 @@ func (profile *Profile) parseConfig() error { } // New returns a new Profile. -func New(source profileSource, id string, linkedPath string) *Profile { +// Optionally, you may supply custom configuration in the flat (key=value) form. +func New( + source profileSource, + id string, + linkedPath string, + customConfig map[string]interface{}, +) *Profile { + if customConfig != nil { + customConfig = config.Expand(customConfig) + } else { + customConfig = make(map[string]interface{}) + } + profile := &Profile{ ID: id, Source: source, LinkedPath: linkedPath, Created: time.Now().Unix(), - Config: make(map[string]interface{}), + Config: customConfig, } // Generate random ID if none is given. @@ -210,9 +222,13 @@ func New(source profileSource, id string, linkedPath string) *Profile { // Make key from ID and source. profile.makeKey() - // Prepare profile to create placeholders. - _ = profile.prepConfig() - _ = profile.parseConfig() + // Prepare and parse initial profile config. + if err := profile.prepConfig(); err != nil { + log.Errorf("profile: failed to prep new profile: %s", err) + } + if err := profile.parseConfig(); err != nil { + log.Errorf("profile: failed to parse new profile: %s", err) + } return profile } @@ -372,7 +388,7 @@ func EnsureProfile(r record.Record) (*Profile, error) { // the profile was changed. If there is data that needs to be fetched from the // operating system, it will start an async worker to fetch that data and save // the profile afterwards. -func (profile *Profile) UpdateMetadata(processName, binaryPath string) (changed bool) { +func (profile *Profile) UpdateMetadata(binaryPath string) (changed bool) { // Check if this is a local profile, else warn and return. if profile.Source != SourceLocal { log.Warningf("tried to update metadata for non-local profile %s", profile.ScopedID()) @@ -382,22 +398,9 @@ func (profile *Profile) UpdateMetadata(processName, binaryPath string) (changed profile.Lock() defer profile.Unlock() - // Check if this is a special profile. - if binaryPath == "" { - // This is a special profile, just assign the processName, if needed, and - // return. - if profile.Name != processName { - profile.Name = processName - return true - } - return false - } - - // Update LinkedPath if if differs from the process path. - // This will (at the moment) only be the case for the Portmaster profile. - if profile.LinkedPath != binaryPath { - profile.LinkedPath = binaryPath - changed = true + // Update special profile and return if it was one. + if ok, changed := updateSpecialProfileMetadata(profile, binaryPath); ok { + return changed } var needsUpdateFromSystem bool diff --git a/profile/special.go b/profile/special.go new file mode 100644 index 00000000..754cdc67 --- /dev/null +++ b/profile/special.go @@ -0,0 +1,109 @@ +package profile + +const ( + // UnidentifiedProfileID is the profile ID used for unidentified processes. + UnidentifiedProfileID = "_unidentified" + // UnidentifiedProfileName is the name used for unidentified processes. + UnidentifiedProfileName = "Unidentified Processes" + + // SystemProfileID is the profile ID used for the system/kernel. + SystemProfileID = "_system" + // SystemProfileName is the name used for the system/kernel. + SystemProfileName = "Operating System" + + // PortmasterProfileID is the profile ID used for the Portmaster Core itself. + PortmasterProfileID = "_portmaster" + // PortmasterProfileName is the name used for the Portmaster Core itself. + PortmasterProfileName = "Portmaster Core Service" + + // PortmasterAppProfileID is the profile ID used for the Portmaster App. + PortmasterAppProfileID = "_portmaster-app" + // PortmasterAppProfileName is the name used for the Portmaster App. + PortmasterAppProfileName = "Portmaster User Interface" + + // PortmasterNotifierProfileID is the profile ID used for the Portmaster Notifier. + PortmasterNotifierProfileID = "_portmaster-notifier" + // PortmasterNotifierProfileName is the name used for the Portmaster Notifier. + PortmasterNotifierProfileName = "Portmaster Notifier" +) + +func updateSpecialProfileMetadata(profile *Profile, binaryPath string) (ok, changed bool) { + // Get new profile name and check if profile is applicable to special handling. + var newProfileName string + switch profile.ID { + case UnidentifiedProfileID: + newProfileName = UnidentifiedProfileName + case SystemProfileID: + newProfileName = SystemProfileName + case PortmasterProfileID: + newProfileName = PortmasterProfileName + case PortmasterAppProfileID: + newProfileName = PortmasterAppProfileName + case PortmasterNotifierProfileID: + newProfileName = PortmasterNotifierProfileName + default: + return false, false + } + + // Update profile name if needed. + if profile.Name != newProfileName { + profile.Name = newProfileName + changed = true + } + + // Update LinkedPath to new value. + if profile.LinkedPath != binaryPath { + profile.LinkedPath = binaryPath + changed = true + } + + return true, changed +} + +func getSpecialProfile(profileID, linkedPath string) *Profile { + switch profileID { + case UnidentifiedProfileID: + return New(SourceLocal, UnidentifiedProfileID, linkedPath, nil) + + case SystemProfileID: + return New(SourceLocal, SystemProfileID, linkedPath, nil) + + case PortmasterProfileID: + profile := New(SourceLocal, PortmasterProfileID, linkedPath, nil) + profile.Internal = true + return profile + + case PortmasterAppProfileID: + profile := New( + SourceLocal, + PortmasterAppProfileID, + linkedPath, + map[string]interface{}{ + CfgOptionDefaultActionKey: "block", + CfgOptionEndpointsKey: []string{ + "+ Localhost", + }, + }, + ) + profile.Internal = true + return profile + + case PortmasterNotifierProfileID: + profile := New( + SourceLocal, + PortmasterNotifierProfileID, + linkedPath, + map[string]interface{}{ + CfgOptionDefaultActionKey: "block", + CfgOptionEndpointsKey: []string{ + "+ Localhost", + }, + }, + ) + profile.Internal = true + return profile + + default: + return nil + } +} diff --git a/status/provider.go b/status/provider.go index 863b11be..e2230f65 100644 --- a/status/provider.go +++ b/status/provider.go @@ -34,7 +34,7 @@ func setupRuntimeProvider() (err error) { return nil } -// setSelectedSecurityLevel updates the selected security level +// setSelectedSecurityLevel updates the selected security level. func setSelectedSecurityLevel(r record.Record) (record.Record, error) { var upd *SelectedSecurityLevelRecord if r.IsWrapped() { diff --git a/status/records.go b/status/records.go index 73801c62..7f2c39b4 100644 --- a/status/records.go +++ b/status/records.go @@ -33,7 +33,7 @@ type SystemStatusRecord struct { // SelectedSecurityLevelRecord is used as a dummy record.Record // to provide a simply runtime-configuration for the user. -// It is write-only and exposed at runtime:system/security-level +// It is write-only and exposed at "runtime:system/security-level". type SelectedSecurityLevelRecord struct { record.Base sync.Mutex diff --git a/status/security_level.go b/status/security_level.go index ea8badb7..03a0f4e0 100644 --- a/status/security_level.go +++ b/status/security_level.go @@ -15,7 +15,7 @@ type ( // It's meant to be used as a value for config.DisplayHintAnnotation. const DisplayHintSecurityLevel string = "security level" -// Security levels +// Security levels. const ( SecurityLevelOff uint8 = 0 SecurityLevelNormal uint8 = 1 @@ -31,24 +31,28 @@ const ( // SecurityLevelValues defines all possible security levels. var SecurityLevelValues = []config.PossibleValue{ { - Name: "Normal", - Value: SecurityLevelsAll, + Name: "Trusted / Home Network", + Value: SecurityLevelsAll, + Description: "Setting is always enabled.", }, { - Name: "High", - Value: SecurityLevelsHighAndExtreme, + Name: "Untrusted / Public Network", + Value: SecurityLevelsHighAndExtreme, + Description: "Setting is enabled in untrusted and dangerous networks.", }, { - Name: "Extreme", - Value: SecurityLevelExtreme, + Name: "Danger / Hacked Network", + Value: SecurityLevelExtreme, + Description: "Setting is enabled only in dangerous networks.", }, } // AllSecurityLevelValues is like SecurityLevelValues but also includes Off. var AllSecurityLevelValues = append([]config.PossibleValue{ { - Name: "Off", - Value: SecurityLevelOff, + Name: "Off", + Value: SecurityLevelOff, + Description: "Setting is always disabled.", }, }, SecurityLevelValues...,