mirror of
https://github.com/safing/portmaster
synced 2025-09-01 18:19:12 +00:00
188 lines
4 KiB
Go
188 lines
4 KiB
Go
package profile
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"sync"
|
|
|
|
"github.com/safing/portmaster/status"
|
|
)
|
|
|
|
// Set handles Profile chaining.
|
|
type Set struct {
|
|
sync.Mutex
|
|
|
|
id string
|
|
profiles [4]*Profile
|
|
// Application
|
|
// Global
|
|
// Stamp
|
|
// Default
|
|
|
|
combinedSecurityLevel uint8
|
|
independent bool
|
|
}
|
|
|
|
// NewSet returns a new profile set with given the profiles.
|
|
func NewSet(ctx context.Context, id string, user, stamp *Profile) *Set {
|
|
new := &Set{
|
|
id: id,
|
|
profiles: [4]*Profile{
|
|
user, // Application
|
|
nil, // Global
|
|
stamp, // Stamp
|
|
nil, // Default
|
|
},
|
|
}
|
|
activateProfileSet(ctx, new)
|
|
new.Update(status.SecurityLevelFortress)
|
|
return new
|
|
}
|
|
|
|
// UserProfile returns the user profile.
|
|
func (set *Set) UserProfile() *Profile {
|
|
return set.profiles[0]
|
|
}
|
|
|
|
// Update gets the new global and default profile and updates the independence status. It must be called when reusing a profile set for a series of calls.
|
|
func (set *Set) Update(securityLevel uint8) {
|
|
set.Lock()
|
|
|
|
specialProfileLock.RLock()
|
|
defer specialProfileLock.RUnlock()
|
|
|
|
// update profiles
|
|
set.profiles[1] = globalProfile
|
|
set.profiles[3] = fallbackProfile
|
|
|
|
// update security level
|
|
profileSecurityLevel := set.getSecurityLevel()
|
|
if profileSecurityLevel > securityLevel {
|
|
set.combinedSecurityLevel = profileSecurityLevel
|
|
} else {
|
|
set.combinedSecurityLevel = securityLevel
|
|
}
|
|
|
|
set.Unlock()
|
|
// update independence
|
|
if set.CheckFlag(Independent) {
|
|
set.Lock()
|
|
set.independent = true
|
|
set.Unlock()
|
|
} else {
|
|
set.Lock()
|
|
set.independent = false
|
|
set.Unlock()
|
|
}
|
|
}
|
|
|
|
// SecurityLevel returns the applicable security level for the profile set.
|
|
func (set *Set) SecurityLevel() uint8 {
|
|
set.Lock()
|
|
defer set.Unlock()
|
|
|
|
return set.combinedSecurityLevel
|
|
}
|
|
|
|
// GetProfileMode returns the active profile mode.
|
|
func (set *Set) GetProfileMode() uint8 {
|
|
switch {
|
|
case set.CheckFlag(Whitelist):
|
|
return Whitelist
|
|
case set.CheckFlag(Prompt):
|
|
return Prompt
|
|
case set.CheckFlag(Blacklist):
|
|
return Blacklist
|
|
default:
|
|
return Whitelist
|
|
}
|
|
}
|
|
|
|
// CheckFlag returns whether a given flag is set.
|
|
func (set *Set) CheckFlag(flag uint8) (active bool) {
|
|
set.Lock()
|
|
defer set.Unlock()
|
|
|
|
for i, profile := range set.profiles {
|
|
if i == 2 && set.independent {
|
|
continue
|
|
}
|
|
|
|
if profile != nil {
|
|
active, ok := profile.Flags.Check(flag, set.combinedSecurityLevel)
|
|
if ok {
|
|
return active
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// CheckEndpointDomain checks if the given endpoint matches an entry in the corresponding list. This is for outbound communication only.
|
|
func (set *Set) CheckEndpointDomain(domain string) (result EPResult, reason string) {
|
|
set.Lock()
|
|
defer set.Unlock()
|
|
|
|
for i, profile := range set.profiles {
|
|
if i == 2 && set.independent {
|
|
continue
|
|
}
|
|
|
|
if profile != nil {
|
|
if result, reason = profile.Endpoints.CheckDomain(domain); result != NoMatch {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
return NoMatch, ""
|
|
}
|
|
|
|
// CheckEndpointIP checks if the given endpoint matches an entry in the corresponding list.
|
|
func (set *Set) CheckEndpointIP(domain string, ip net.IP, protocol uint8, port uint16, inbound bool) (result EPResult, reason string) {
|
|
set.Lock()
|
|
defer set.Unlock()
|
|
|
|
for i, profile := range set.profiles {
|
|
if i == 2 && set.independent {
|
|
continue
|
|
}
|
|
|
|
if profile != nil {
|
|
if inbound {
|
|
if result, reason = profile.ServiceEndpoints.CheckIP(domain, ip, protocol, port, inbound, set.combinedSecurityLevel); result != NoMatch {
|
|
return
|
|
}
|
|
} else {
|
|
if result, reason = profile.Endpoints.CheckIP(domain, ip, protocol, port, inbound, set.combinedSecurityLevel); result != NoMatch {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NoMatch, ""
|
|
}
|
|
|
|
// getSecurityLevel returns the highest prioritized security level.
|
|
func (set *Set) getSecurityLevel() uint8 {
|
|
if set == nil {
|
|
return 0
|
|
}
|
|
|
|
for i, profile := range set.profiles {
|
|
if i == 2 {
|
|
// Stamp profiles do not have the SecurityLevel setting
|
|
continue
|
|
}
|
|
|
|
if profile != nil {
|
|
if profile.SecurityLevel > 0 {
|
|
return profile.SecurityLevel
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0
|
|
}
|