mirror of
https://github.com/safing/portmaster
synced 2025-04-25 13:29:10 +00:00
Work on pm restructure
This commit is contained in:
parent
62b1c03edc
commit
8fb21fd900
23 changed files with 487 additions and 326 deletions
process
profile
|
@ -6,6 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/Safing/portbase/database"
|
||||
"github.com/Safing/portmaster/profile"
|
||||
"github.com/tevino/abool"
|
||||
)
|
||||
|
||||
|
@ -76,6 +77,8 @@ func (p *Process) Delete() {
|
|||
if dbControllerFlag.IsSet() {
|
||||
dbController.PushUpdate(p)
|
||||
}
|
||||
|
||||
profile.DeactivateProfileSet(p.profileSet)
|
||||
}
|
||||
|
||||
// CleanProcessStorage cleans the storage from old processes.
|
||||
|
|
|
@ -1,22 +1,81 @@
|
|||
package process
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Safing/portbase/database"
|
||||
"github.com/Safing/portbase/log"
|
||||
"github.com/Safing/portmaster/profile"
|
||||
"github.com/Safing/portmaster/profile/index"
|
||||
)
|
||||
|
||||
// FindProfiles finds and assigns a profile set to the process.
|
||||
func (p *Process) FindProfiles() {
|
||||
func (p *Process) FindProfiles() error {
|
||||
|
||||
// Get fingerprints of process
|
||||
|
||||
// Check if user profile already exists, else create new
|
||||
pathIdentifier := profile.GetPathIdentifier(p.Path)
|
||||
indexRecord, err := index.Get(pathIdentifier)
|
||||
if err != nil && err != database.ErrNotFound {
|
||||
log.Errorf("process: could not get profile index for %s: %s", pathIdentifier, err)
|
||||
}
|
||||
|
||||
var possibleProfiles []*profile.Profile
|
||||
if indexRecord != nil {
|
||||
for _, profileID := range indexRecord.UserProfiles {
|
||||
prof, err := profile.Get(profileID)
|
||||
if err != nil {
|
||||
log.Errorf("process: failed to load profile %s: %s", profileID, err)
|
||||
}
|
||||
possibleProfiles = append(possibleProfiles, prof)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prof := selectProfile(p, possibleProfiles)
|
||||
if prof == nil {
|
||||
// create new profile
|
||||
prof := profile.New()
|
||||
prof.Name = p.ExecName
|
||||
prof.AddFingerprint(&profile.Fingerprint{
|
||||
Type: "full_path",
|
||||
Value: p.Path,
|
||||
})
|
||||
// TODO: maybe add sha256_sum?
|
||||
prof.MarkUsed()
|
||||
prof.Save()
|
||||
}
|
||||
|
||||
// Find/Re-evaluate Stamp profile
|
||||
// 1. check linked stamp profile
|
||||
// 2. if last check is was more than a week ago, fetch from stamp:
|
||||
// 3. send path identifier to stamp
|
||||
// 4. evaluate all returned profiles
|
||||
// 5. select best
|
||||
// 6. link stamp profile to user profile
|
||||
// FIXME: implement!
|
||||
|
||||
// p.UserProfileKey
|
||||
// p.profileSet
|
||||
if prof.MarkUsed() {
|
||||
prof.Save()
|
||||
}
|
||||
|
||||
p.UserProfileKey = prof.Key()
|
||||
p.profileSet = profile.NewSet(prof, nil)
|
||||
p.Save()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func selectProfile(p *Process, profs []*profile.Profile) (selectedProfile *profile.Profile) {
|
||||
var highestScore int
|
||||
for _, prof := range profs {
|
||||
score := matchProfile(p, prof)
|
||||
if score > highestScore {
|
||||
selectedProfile = prof
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func matchProfile(p *Process, prof *profile.Profile) (score int) {
|
||||
|
@ -37,8 +96,10 @@ func matchFingerprint(p *Process, fp *profile.Fingerprint) (score int) {
|
|||
}
|
||||
return profile.GetFingerprintWeight(fp.Type)
|
||||
case "partial_path":
|
||||
// FIXME: if full_path matches, do not match partial paths
|
||||
return profile.GetFingerprintWeight(fp.Type)
|
||||
case "md5_sum", "sha1_sum", "sha256_sum":
|
||||
// FIXME: one sum is enough, check sums in a grouped form, start with the best
|
||||
sum, err := p.GetExecHash(fp.Type)
|
||||
if err != nil {
|
||||
log.Errorf("process: failed to get hash of executable: %s", err)
|
||||
|
|
66
profile/active.go
Normal file
66
profile/active.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
package profile
|
||||
|
||||
import "sync"
|
||||
|
||||
var (
|
||||
activeProfileSets = make(map[string]*Set)
|
||||
activeProfileSetsLock sync.RWMutex
|
||||
)
|
||||
|
||||
func activateProfileSet(set *Set) {
|
||||
set.Lock()
|
||||
defer set.Unlock()
|
||||
activeProfileSetsLock.Lock()
|
||||
defer activeProfileSetsLock.Unlock()
|
||||
activeProfileSets[set.profiles[0].ID] = set
|
||||
}
|
||||
|
||||
// DeactivateProfileSet marks a profile set as not active.
|
||||
func DeactivateProfileSet(set *Set) {
|
||||
set.Lock()
|
||||
defer set.Unlock()
|
||||
activeProfileSetsLock.Lock()
|
||||
defer activeProfileSetsLock.Unlock()
|
||||
delete(activeProfileSets, set.profiles[0].ID)
|
||||
}
|
||||
|
||||
func updateActiveUserProfile(profile *Profile) {
|
||||
activeProfileSetsLock.RLock()
|
||||
defer activeProfileSetsLock.RUnlock()
|
||||
activeSet, ok := activeProfileSets[profile.ID]
|
||||
if ok {
|
||||
activeSet.Lock()
|
||||
defer activeSet.Unlock()
|
||||
activeSet.profiles[0] = profile
|
||||
}
|
||||
}
|
||||
|
||||
func updateActiveGlobalProfile(profile *Profile) {
|
||||
updateActiveProfile(1, profile)
|
||||
}
|
||||
|
||||
func updateActiveStampProfile(profile *Profile) {
|
||||
updateActiveProfile(2, profile)
|
||||
}
|
||||
|
||||
func updateActiveFallbackProfile(profile *Profile) {
|
||||
updateActiveProfile(3, profile)
|
||||
}
|
||||
|
||||
func updateActiveProfile(setID int, profile *Profile) {
|
||||
activeProfileSetsLock.RLock()
|
||||
defer activeProfileSetsLock.RUnlock()
|
||||
|
||||
for _, activeSet := range activeProfileSets {
|
||||
activeSet.Lock()
|
||||
activeProfile := activeSet.profiles[setID]
|
||||
if activeProfile != nil {
|
||||
activeProfile.Lock()
|
||||
if activeProfile.ID == profile.ID {
|
||||
activeSet.profiles[setID] = profile
|
||||
}
|
||||
activeProfile.Unlock()
|
||||
}
|
||||
activeSet.Unlock()
|
||||
}
|
||||
}
|
|
@ -2,8 +2,7 @@ package profile
|
|||
|
||||
// Platform identifiers
|
||||
const (
|
||||
PlatformAny = "any"
|
||||
PlatformLinux = "lin"
|
||||
PlatformWindows = "win"
|
||||
PlatformMac = "mac"
|
||||
PlatformLinux = "linux"
|
||||
PlatformWindows = "windows"
|
||||
PlatformMac = "macos"
|
||||
)
|
||||
|
|
|
@ -2,5 +2,5 @@ package profile
|
|||
|
||||
// OS Identifier
|
||||
const (
|
||||
osIdentifier = "mac"
|
||||
osIdentifier = PlatformMac
|
||||
)
|
||||
|
|
|
@ -2,5 +2,5 @@ package profile
|
|||
|
||||
// OS Identifier
|
||||
const (
|
||||
osIdentifier = "lin"
|
||||
osIdentifier = PlatformLinux
|
||||
)
|
||||
|
|
|
@ -12,8 +12,9 @@ import (
|
|||
|
||||
// Namespaces
|
||||
const (
|
||||
UserNamespace = "user"
|
||||
StampNamespace = "stamp"
|
||||
userNamespace = "user"
|
||||
stampNamespace = "stamp"
|
||||
specialNamespace = "special"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
82
profile/defaults.go
Normal file
82
profile/defaults.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
package profile
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Safing/portmaster/status"
|
||||
)
|
||||
|
||||
func makeDefaultGlobalProfile() *Profile {
|
||||
return &Profile{
|
||||
ID: "global",
|
||||
Name: "Global Profile",
|
||||
}
|
||||
}
|
||||
|
||||
func makeDefaultFallbackProfile() *Profile {
|
||||
return &Profile{
|
||||
ID: "fallback",
|
||||
Name: "Fallback Profile",
|
||||
Flags: map[uint8]uint8{
|
||||
// Profile Modes
|
||||
Blacklist: status.SecurityLevelDynamic,
|
||||
Prompt: status.SecurityLevelSecure,
|
||||
Whitelist: status.SecurityLevelFortress,
|
||||
|
||||
// Network Locations
|
||||
Internet: status.SecurityLevelsDynamicAndSecure,
|
||||
LAN: status.SecurityLevelsDynamicAndSecure,
|
||||
Localhost: status.SecurityLevelsAll,
|
||||
|
||||
// Specials
|
||||
Related: status.SecurityLevelDynamic,
|
||||
PeerToPeer: status.SecurityLevelDynamic,
|
||||
},
|
||||
Ports: map[int16][]*Port{
|
||||
6: []*Port{
|
||||
&Port{ // SSH
|
||||
Permit: true,
|
||||
Created: time.Now().Unix(),
|
||||
Start: 22,
|
||||
End: 22,
|
||||
},
|
||||
&Port{ // HTTP
|
||||
Permit: true,
|
||||
Created: time.Now().Unix(),
|
||||
Start: 80,
|
||||
End: 80,
|
||||
},
|
||||
&Port{ // HTTPS
|
||||
Permit: true,
|
||||
Created: time.Now().Unix(),
|
||||
Start: 443,
|
||||
End: 443,
|
||||
},
|
||||
&Port{ // SMTP (TLS)
|
||||
Permit: true,
|
||||
Created: time.Now().Unix(),
|
||||
Start: 465,
|
||||
End: 465,
|
||||
},
|
||||
&Port{ // SMTP (STARTTLS)
|
||||
Permit: true,
|
||||
Created: time.Now().Unix(),
|
||||
Start: 587,
|
||||
End: 587,
|
||||
},
|
||||
&Port{ // IMAP (TLS)
|
||||
Permit: true,
|
||||
Created: time.Now().Unix(),
|
||||
Start: 993,
|
||||
End: 993,
|
||||
},
|
||||
&Port{ // IMAP (STARTTLS)
|
||||
Permit: true,
|
||||
Created: time.Now().Unix(),
|
||||
Start: 143,
|
||||
End: 143,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -63,14 +63,14 @@ func GetFingerprintWeight(fpType string) (weight int) {
|
|||
// return
|
||||
// }
|
||||
//
|
||||
// func (p *Profile) AddFingerprint(fp *Fingerprint) error {
|
||||
// if fp.OS == "" {
|
||||
// fp.OS = osIdentifier
|
||||
// }
|
||||
//
|
||||
// p.Fingerprints = append(p.Fingerprints, fp)
|
||||
// return p.Save()
|
||||
// }
|
||||
func (p *Profile) AddFingerprint(fp *Fingerprint) {
|
||||
if fp.OS == "" {
|
||||
fp.OS = osIdentifier
|
||||
}
|
||||
|
||||
p.Fingerprints = append(p.Fingerprints, fp)
|
||||
}
|
||||
|
||||
//
|
||||
// func (p *Profile) GetApplicableFingerprintTypes() (types []string) {
|
||||
// for _, fp := range p.Fingerprints {
|
||||
|
|
47
profile/identifier_linux.go
Normal file
47
profile/identifier_linux.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package profile
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Safing/portbase/utils"
|
||||
)
|
||||
|
||||
// GetPathIdentifier returns the identifier from the given path
|
||||
func GetPathIdentifier(path string) string {
|
||||
// clean path
|
||||
// TODO: is this necessary?
|
||||
cleanedPath, err := filepath.EvalSymlinks(path)
|
||||
if err == nil {
|
||||
path = cleanedPath
|
||||
} else {
|
||||
path = filepath.Clean(path)
|
||||
}
|
||||
|
||||
splittedPath := strings.Split(path, "/")
|
||||
|
||||
// strip sensitive data
|
||||
switch {
|
||||
case strings.HasPrefix(path, "/home/"):
|
||||
splittedPath = splittedPath[3:]
|
||||
case strings.HasPrefix(path, "/root/"):
|
||||
splittedPath = splittedPath[2:]
|
||||
}
|
||||
|
||||
// common directories with executable
|
||||
if i := utils.IndexOfString(splittedPath, "bin"); i > 0 {
|
||||
splittedPath = splittedPath[i:]
|
||||
return strings.Join(splittedPath, "/")
|
||||
}
|
||||
if i := utils.IndexOfString(splittedPath, "sbin"); i > 0 {
|
||||
splittedPath = splittedPath[i:]
|
||||
return strings.Join(splittedPath, "/")
|
||||
}
|
||||
|
||||
// shorten to max 3
|
||||
if len(splittedPath) > 3 {
|
||||
splittedPath = splittedPath[len(splittedPath)-3:]
|
||||
}
|
||||
|
||||
return strings.Join(splittedPath, "/")
|
||||
}
|
23
profile/identifier_linux_test.go
Normal file
23
profile/identifier_linux_test.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package profile
|
||||
|
||||
import "testing"
|
||||
|
||||
func testPathID(t *testing.T, execPath, identifierPath string) {
|
||||
result := GetPathIdentifier(execPath)
|
||||
if result != identifierPath {
|
||||
t.Errorf("unexpected identifier path for %s: got %s, expected %s", execPath, result, identifierPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPathIdentifier(t *testing.T) {
|
||||
testPathID(t, "/bin/bash", "bin/bash")
|
||||
testPathID(t, "/home/user/bin/bash", "bin/bash")
|
||||
testPathID(t, "/home/user/project/main", "project/main")
|
||||
testPathID(t, "/root/project/main", "project/main")
|
||||
testPathID(t, "/tmp/a/b/c/d/install.sh", "c/d/install.sh")
|
||||
testPathID(t, "/sbin/init", "sbin/init")
|
||||
testPathID(t, "/lib/systemd/systemd-udevd", "lib/systemd/systemd-udevd")
|
||||
testPathID(t, "/bundle/ruby/2.4.0/bin/passenger", "bin/passenger")
|
||||
testPathID(t, "/usr/sbin/cron", "sbin/cron")
|
||||
testPathID(t, "/usr/local/bin/python", "bin/python")
|
||||
}
|
|
@ -1,27 +1,26 @@
|
|||
package index
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"fmt"
|
||||
"errors"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/Safing/portbase/database/record"
|
||||
"github.com/Safing/portbase/utils"
|
||||
"github.com/Safing/portbase/database/record"
|
||||
"github.com/Safing/portbase/utils"
|
||||
)
|
||||
|
||||
// ProfileIndex links an Identifier to Profiles
|
||||
type ProfileIndex struct {
|
||||
record.Base
|
||||
sync.Mutex
|
||||
record.Base
|
||||
sync.Mutex
|
||||
|
||||
ID string
|
||||
UserProfiles []string
|
||||
StampProfiles []string
|
||||
UserProfiles []string
|
||||
StampProfiles []string
|
||||
}
|
||||
|
||||
func makeIndexRecordKey(id string) string {
|
||||
return fmt.Sprintf("core:profiles/index/%s", base64.RawURLEncoding.EncodeToString([]byte(id)))
|
||||
return fmt.Sprintf("index:profiles/%s", base64.RawURLEncoding.EncodeToString([]byte(id)))
|
||||
}
|
||||
|
||||
// NewIndex returns a new ProfileIndex.
|
||||
|
@ -32,35 +31,35 @@ func NewIndex(id string) *ProfileIndex {
|
|||
}
|
||||
|
||||
// AddUserProfile adds a User Profile to the index.
|
||||
func (pi *ProfileIndex) AddUserProfile(id string) (changed bool) {
|
||||
if !utils.StringInSlice(pi.UserProfiles, id) {
|
||||
pi.UserProfiles = append(pi.UserProfiles, id)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
func (pi *ProfileIndex) AddUserProfile(identifier string) (changed bool) {
|
||||
if !utils.StringInSlice(pi.UserProfiles, id) {
|
||||
pi.UserProfiles = append(pi.UserProfiles, id)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AddStampProfile adds a Stamp Profile to the index.
|
||||
func (pi *ProfileIndex) AddStampProfile(id string) (changed bool) {
|
||||
if !utils.StringInSlice(pi.StampProfiles, id) {
|
||||
pi.StampProfiles = append(pi.StampProfiles, id)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
func (pi *ProfileIndex) AddStampProfile(identifier string) (changed bool) {
|
||||
if !utils.StringInSlice(pi.StampProfiles, id) {
|
||||
pi.StampProfiles = append(pi.StampProfiles, id)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RemoveUserProfile removes a profile from the index.
|
||||
func (pi *ProfileIndex) RemoveUserProfile(id string) {
|
||||
pi.UserProfiles = utils.RemoveFromStringSlice(pi.UserProfiles, id)
|
||||
pi.UserProfiles = utils.RemoveFromStringSlice(pi.UserProfiles, id)
|
||||
}
|
||||
|
||||
// RemoveStampProfile removes a profile from the index.
|
||||
func (pi *ProfileIndex) RemoveStampProfile(id string) {
|
||||
pi.StampProfiles = utils.RemoveFromStringSlice(pi.StampProfiles, id)
|
||||
pi.StampProfiles = utils.RemoveFromStringSlice(pi.StampProfiles, id)
|
||||
}
|
||||
|
||||
// GetIndex gets a ProfileIndex from the database.
|
||||
func GetIndex(id string) (*ProfileIndex, error) {
|
||||
// Get gets a ProfileIndex from the database.
|
||||
func Get(id string) (*ProfileIndex, error) {
|
||||
key := makeIndexRecordKey(id)
|
||||
|
||||
r, err := indexDB.Get(key)
|
||||
|
@ -89,13 +88,13 @@ func GetIndex(id string) (*ProfileIndex, error) {
|
|||
|
||||
// Save saves the Identifiers to the database
|
||||
func (pi *ProfileIndex) Save() error {
|
||||
if pi.Key() == "" {
|
||||
if pi.ID != "" {
|
||||
pi.SetKey(makeIndexRecordKey(pi.ID))
|
||||
} else {
|
||||
return errors.New("missing identification Key")
|
||||
}
|
||||
}
|
||||
if pi.Key() == "" {
|
||||
if pi.ID != "" {
|
||||
pi.SetKey(makeIndexRecordKey(pi.ID))
|
||||
} else {
|
||||
return errors.New("missing identification Key")
|
||||
}
|
||||
}
|
||||
|
||||
return indexDB.Put(pi)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package matcher
|
||||
package matching
|
||||
|
||||
import (
|
||||
"github.com/Safing/portbase/database"
|
3
profile/module.go
Normal file
3
profile/module.go
Normal file
|
@ -0,0 +1,3 @@
|
|||
package profile
|
||||
|
||||
.
|
|
@ -3,6 +3,7 @@ package profile
|
|||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
uuid "github.com/satori/go.uuid"
|
||||
|
||||
|
@ -10,6 +11,10 @@ import (
|
|||
"github.com/Safing/portmaster/status"
|
||||
)
|
||||
|
||||
var (
|
||||
lastUsedUpdateThreshold = 1 * time.Hour
|
||||
)
|
||||
|
||||
// Profile is used to predefine a security profile for applications.
|
||||
type Profile struct {
|
||||
record.Base
|
||||
|
@ -24,7 +29,7 @@ type Profile struct {
|
|||
Icon string
|
||||
|
||||
// Fingerprints
|
||||
Fingerprints []string
|
||||
Fingerprints []*Fingerprint
|
||||
|
||||
// The mininum security level to apply to connections made with this profile
|
||||
SecurityLevel uint8
|
||||
|
@ -47,6 +52,10 @@ func New() *Profile {
|
|||
return &Profile{}
|
||||
}
|
||||
|
||||
func makeProfileKey(namespace, ID string) string {
|
||||
return fmt.Sprintf("core:profiles/%s/%s", namespace, ID)
|
||||
}
|
||||
|
||||
// Save saves the profile to the database
|
||||
func (profile *Profile) Save(namespace string) error {
|
||||
if profile.ID == "" {
|
||||
|
@ -61,12 +70,21 @@ func (profile *Profile) Save(namespace string) error {
|
|||
if namespace == "" {
|
||||
return fmt.Errorf("no key or namespace defined for profile %s", profile.String())
|
||||
}
|
||||
profile.SetKey(fmt.Sprintf("config:profiles/%s/%s", namespace, profile.ID))
|
||||
profile.SetKey(makeProfileKey(namespace, profile.ID))
|
||||
}
|
||||
|
||||
return profileDB.Put(profile)
|
||||
}
|
||||
|
||||
// MarkUsed marks the profile as used, eventually.
|
||||
func (profile *Profile) MarkUsed() (updated bool) {
|
||||
if time.Now().Add(-lastUsedUpdateThreshold).Unix() > profile.ApproxLastUsed {
|
||||
profile.ApproxLastUsed = time.Now().Unix()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// String returns a string representation of the Profile.
|
||||
func (profile *Profile) String() string {
|
||||
return profile.Name
|
||||
|
@ -79,10 +97,38 @@ func (profile *Profile) DetailedString() string {
|
|||
|
||||
// GetUserProfile loads a profile from the database.
|
||||
func GetUserProfile(ID string) (*Profile, error) {
|
||||
return nil, nil
|
||||
return getProfile(userNamespace, ID)
|
||||
}
|
||||
|
||||
// GetStampProfile loads a profile from the database.
|
||||
func GetStampProfile(ID string) (*Profile, error) {
|
||||
return nil, nil
|
||||
return getProfile(stampNamespace, ID)
|
||||
}
|
||||
|
||||
func getProfile(namespace, ID string) (*Profile, error) {
|
||||
r, err := profileDB.Get(makeProfileKey(namespace, ID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ensureProfile(r)
|
||||
}
|
||||
|
||||
func ensureProfile(r record.Record) (*Profile, error) {
|
||||
// unwrap
|
||||
if r.IsWrapped() {
|
||||
// only allocate a new struct, if we need it
|
||||
new := &Profile{}
|
||||
err := record.Unwrap(r, new)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return new, nil
|
||||
}
|
||||
|
||||
// or adjust type
|
||||
new, ok := r.(*Profile)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("record not of type *Example, but %T", r)
|
||||
}
|
||||
return new, nil
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package profile
|
||||
|
||||
import "github.com/Safing/portmaster/status"
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/Safing/portmaster/status"
|
||||
)
|
||||
|
||||
var (
|
||||
emptyFlags = Flags{}
|
||||
|
@ -9,6 +13,8 @@ var (
|
|||
|
||||
// Set handles Profile chaining.
|
||||
type Set struct {
|
||||
sync.Mutex
|
||||
|
||||
profiles [4]*Profile
|
||||
// Application
|
||||
// Global
|
||||
|
@ -29,6 +35,7 @@ func NewSet(user, stamp *Profile) *Set {
|
|||
nil, // Default
|
||||
},
|
||||
}
|
||||
activateProfileSet(new)
|
||||
new.Update(status.SecurityLevelFortress)
|
||||
return new
|
||||
}
|
||||
|
@ -40,7 +47,7 @@ func (set *Set) Update(securityLevel uint8) {
|
|||
|
||||
// update profiles
|
||||
set.profiles[1] = globalProfile
|
||||
set.profiles[3] = defaultProfile
|
||||
set.profiles[3] = fallbackProfile
|
||||
|
||||
// update security level
|
||||
profileSecurityLevel := set.getProfileSecurityLevel()
|
||||
|
@ -96,7 +103,7 @@ func (set *Set) CheckDomain(domain string) (permit, ok bool) {
|
|||
return false, false
|
||||
}
|
||||
|
||||
// Ports returns the highest prioritized Ports configuration.
|
||||
// CheckPort checks if the given protocol and port are governed in any the lists of ports and returns whether it is permitted.
|
||||
func (set *Set) CheckPort(listen bool, protocol uint8, port uint16) (permit, ok bool) {
|
||||
|
||||
signedProtocol := int16(protocol)
|
||||
|
|
|
@ -1,261 +0,0 @@
|
|||
package profile
|
||||
|
||||
// DEACTIVATED
|
||||
|
||||
// import (
|
||||
// "runtime"
|
||||
//
|
||||
// "github.com/Safing/portbase/database"
|
||||
// "github.com/Safing/portbase/log"
|
||||
// )
|
||||
//
|
||||
// func init() {
|
||||
//
|
||||
// // Data here is for demo purposes, Profiles will be served over network soon™.
|
||||
//
|
||||
// log.Tracef("profiles: loading sample profiles for %s", runtime.GOOS)
|
||||
//
|
||||
// switch runtime.GOOS {
|
||||
// case "linux":
|
||||
//
|
||||
// log.Trace("profiles: loading linux sample profiles")
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "Chromium",
|
||||
// Description: "Browser by Google",
|
||||
// Path: "/usr/lib/chromium-browser/chromium-browser",
|
||||
// Flags: []int8{User, Internet, LocalNet, Browser},
|
||||
// ConnectPorts: []uint16{80, 443},
|
||||
// }).CreateInDist()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "Evolution",
|
||||
// Description: "PIM solution by GNOME",
|
||||
// Path: "/usr/bin/evolution",
|
||||
// Flags: []int8{User, Internet, Gateway},
|
||||
// ConnectPorts: []uint16{25, 80, 143, 443, 465, 587, 993, 995},
|
||||
// SecurityLevel: 2,
|
||||
// }).CreateInDist()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "Evolution Calendar",
|
||||
// Description: "PIM solution by GNOME - Calendar",
|
||||
// Path: "/usr/lib/evolution/evolution-calendar-factory-subprocess",
|
||||
// Flags: []int8{User, Internet, Gateway},
|
||||
// ConnectPorts: []uint16{80, 443},
|
||||
// SecurityLevel: 2,
|
||||
// }).CreateInDist()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "Spotify",
|
||||
// Description: "Music streaming",
|
||||
// Path: "/usr/share/spotify/spotify",
|
||||
// ConnectPorts: []uint16{80, 443, 4070},
|
||||
// Flags: []int8{User, Internet, Strict},
|
||||
// }).CreateInDist()
|
||||
//
|
||||
// (&Profile{
|
||||
// // flatpak edition
|
||||
// Name: "Spotify",
|
||||
// Description: "Music streaming",
|
||||
// Path: "/newroot/app/extra/share/spotify/spotify",
|
||||
// ConnectPorts: []uint16{80, 443, 4070},
|
||||
// Flags: []int8{User, Internet, Strict},
|
||||
// }).CreateInDist()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "Evince",
|
||||
// Description: "PDF Document Reader",
|
||||
// Path: "/usr/bin/evince",
|
||||
// Flags: []int8{},
|
||||
// SecurityLevel: 2,
|
||||
// }).CreateInDist()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "Ahavi",
|
||||
// Description: "mDNS service",
|
||||
// Path: "/usr/bin/avahi-daemon",
|
||||
// Flags: []int8{System, LocalNet, Service, Directconnect},
|
||||
// }).CreateInDist()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "Python 2.7 Framework",
|
||||
// Description: "Correctly handle python scripts",
|
||||
// Path: "/usr/bin/python2.7",
|
||||
// Framework: &Framework{
|
||||
// Find: "^[^ ]+ ([^ ]+)",
|
||||
// Build: "{1}|{CWD}/{1}",
|
||||
// },
|
||||
// }).CreateInDist()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "Python 3.5 Framework",
|
||||
// Description: "Correctly handle python scripts",
|
||||
// Path: "/usr/bin/python3.5",
|
||||
// Framework: &Framework{
|
||||
// Find: "^[^ ]+ ([^ ]+)",
|
||||
// Build: "{1}|{CWD}/{1}",
|
||||
// },
|
||||
// }).CreateInDist()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "DHCP Client",
|
||||
// Description: "Client software for the DHCP protocol",
|
||||
// Path: "/sbin/dhclient",
|
||||
// Framework: &Framework{
|
||||
// FindParent: 1,
|
||||
// MergeWithParent: true,
|
||||
// },
|
||||
// }).CreateInDist()
|
||||
//
|
||||
// // Default Profiles
|
||||
// // Until Profiles are distributed over the network, default profiles are activated when the Default Profile for "/" is missing.
|
||||
//
|
||||
// if ok, err := database.Has(ds.NewKey("/Data/Profiles/Profile_d-2f")); !ok || err != nil {
|
||||
//
|
||||
// log.Trace("profiles: loading linux default sample profiles")
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "Default Base",
|
||||
// Description: "Default Profile for /",
|
||||
// Path: "/",
|
||||
// Flags: []int8{Internet, LocalNet, Strict},
|
||||
// Default: true,
|
||||
// }).Create()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "Installed Applications",
|
||||
// Description: "Default Profile for /usr/bin",
|
||||
// Path: "/usr/bin/",
|
||||
// Flags: []int8{Internet, LocalNet, Gateway},
|
||||
// Default: true,
|
||||
// }).Create()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "System Binaries (/sbin)",
|
||||
// Description: "Default Profile for ~/Downloads",
|
||||
// Path: "/sbin/",
|
||||
// Flags: []int8{Internet, LocalNet, Directconnect, Service, System},
|
||||
// Default: true,
|
||||
// }).Create()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "System Binaries (/usr/sbin)",
|
||||
// Description: "Default Profile for ~/Downloads",
|
||||
// Path: "/usr/sbin/",
|
||||
// Flags: []int8{Internet, LocalNet, Directconnect, Service, System},
|
||||
// Default: true,
|
||||
// }).Create()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "System Tmp folder",
|
||||
// Description: "Default Profile for /tmp",
|
||||
// Path: "/tmp/",
|
||||
// Flags: []int8{}, // deny all
|
||||
// Default: true,
|
||||
// }).Create()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "User Home",
|
||||
// Description: "Default Profile for ~/",
|
||||
// Path: "~/",
|
||||
// Flags: []int8{Internet, LocalNet, Gateway},
|
||||
// Default: true,
|
||||
// }).Create()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "User Downloads",
|
||||
// Description: "Default Profile for ~/Downloads",
|
||||
// Path: "~/Downloads/",
|
||||
// Flags: []int8{}, // deny all
|
||||
// Default: true,
|
||||
// }).Create()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "User Cache",
|
||||
// Description: "Default Profile for ~/.cache",
|
||||
// Path: "~/.cache/",
|
||||
// Flags: []int8{}, // deny all
|
||||
// Default: true,
|
||||
// }).Create()
|
||||
//
|
||||
// }
|
||||
//
|
||||
// case "windows":
|
||||
//
|
||||
// log.Trace("profiles: loading windows sample profiles")
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "Firefox",
|
||||
// Description: "Firefox Browser by Mozilla",
|
||||
// Path: "C:\\Program Files\\Mozilla Firefox\\firefox.exe",
|
||||
// Flags: []int8{User, Internet, LocalNet, Browser},
|
||||
// ConnectPorts: []uint16{80, 443},
|
||||
// }).CreateInDist()
|
||||
//
|
||||
// // Default Profiles
|
||||
// // Until Profiles are distributed over the network, default profiles are activated when the Default Profile for "C" is missing.
|
||||
//
|
||||
// if ok, err := database.Has(ds.NewKey("/Data/Profiles/Profile:d-C")); !ok || err != nil {
|
||||
//
|
||||
// log.Trace("profiles: loading windows default sample profiles")
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "Default Base",
|
||||
// Description: "Default Profile for C",
|
||||
// Path: "C",
|
||||
// Flags: []int8{Internet, LocalNet, Strict},
|
||||
// Default: true,
|
||||
// }).Create()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "Installed Applications",
|
||||
// Description: "Default Profile for C:\\Program Files",
|
||||
// Path: "C:\\Program Files\\",
|
||||
// Flags: []int8{Internet, LocalNet, Gateway},
|
||||
// Default: true,
|
||||
// }).Create()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "Installed Applications (x86)",
|
||||
// Description: "Default Profile for C:\\Program Files (x86)",
|
||||
// Path: "C:\\Program Files (x86)\\",
|
||||
// Flags: []int8{Internet, LocalNet, Gateway},
|
||||
// Default: true,
|
||||
// }).Create()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "System Applications (C:\\Windows\\System32)",
|
||||
// Description: "Default Profile for C:\\Windows\\System32",
|
||||
// Path: "C:\\Windows\\System32\\",
|
||||
// Flags: []int8{Internet, LocalNet, Directconnect, Service, System},
|
||||
// Default: true,
|
||||
// }).Create()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "User Home",
|
||||
// Description: "Default Profile for ~/",
|
||||
// Path: "~/",
|
||||
// Flags: []int8{Internet, LocalNet, Gateway},
|
||||
// Default: true,
|
||||
// }).Create()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "User Downloads",
|
||||
// Description: "Default Profile for ~/Downloads",
|
||||
// Path: "~/Downloads/",
|
||||
// Flags: []int8{}, // deny all
|
||||
// Default: true,
|
||||
// }).Create()
|
||||
//
|
||||
// (&Profile{
|
||||
// Name: "User Cache",
|
||||
// Description: "Default Profile for ~/.cache",
|
||||
// Path: "~/.cache/",
|
||||
// Flags: []int8{}, // deny all
|
||||
// Default: true,
|
||||
// }).Create()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
|
@ -1,12 +1,44 @@
|
|||
package profile
|
||||
|
||||
import "sync"
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/Safing/portbase/database"
|
||||
)
|
||||
|
||||
var (
|
||||
globalProfile *Profile
|
||||
defaultProfile *Profile
|
||||
globalProfile *Profile
|
||||
fallbackProfile *Profile
|
||||
|
||||
specialProfileLock sync.RWMutex
|
||||
)
|
||||
|
||||
// FIXME: subscribe to changes and update profiles
|
||||
func initSpecialProfiles() (err error) {
|
||||
|
||||
specialProfileLock.Lock()
|
||||
defer specialProfileLock.Unlock()
|
||||
|
||||
globalProfile, err = getSpecialProfile("global")
|
||||
if err != nil {
|
||||
if err != database.ErrNotFound {
|
||||
return err
|
||||
}
|
||||
globalProfile = makeDefaultGlobalProfile()
|
||||
globalProfile.Save(specialNamespace)
|
||||
}
|
||||
|
||||
fallbackProfile, err = getSpecialProfile("fallback")
|
||||
if err != nil {
|
||||
if err != database.ErrNotFound {
|
||||
return err
|
||||
}
|
||||
fallbackProfile = makeDefaultFallbackProfile()
|
||||
fallbackProfile.Save(specialNamespace)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSpecialProfile(ID string) (*Profile, error) {
|
||||
return getProfile(specialNamespace, ID)
|
||||
}
|
||||
|
|
53
profile/updates.go
Normal file
53
profile/updates.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package profile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Safing/portbase/database"
|
||||
"github.com/Safing/portbase/database/query"
|
||||
"github.com/Safing/portbase/log"
|
||||
)
|
||||
|
||||
func initUpdateListener() error {
|
||||
sub, err := profileDB.Subscribe(query.New(makeProfileKey(specialNamespace, "")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go updateListener(sub)
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
slashedUserNamespace = fmt.Sprintf("/%s/", userNamespace)
|
||||
slashedStampNamespace = fmt.Sprintf("/%s/", stampNamespace)
|
||||
)
|
||||
|
||||
func updateListener(sub *database.Subscription) {
|
||||
for r := range sub.Feed {
|
||||
profile, err := ensureProfile(r)
|
||||
if err != nil {
|
||||
log.Errorf("profile: received update for special profile, but could not read: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
specialProfileLock.Lock()
|
||||
switch profile.ID {
|
||||
case "global":
|
||||
globalProfile = profile
|
||||
updateActiveGlobalProfile(profile)
|
||||
case "fallback":
|
||||
fallbackProfile = profile
|
||||
updateActiveFallbackProfile(profile)
|
||||
default:
|
||||
switch {
|
||||
case strings.HasPrefix(profile.Key(), makeProfileKey(userNamespace, "")):
|
||||
updateActiveUserProfile(profile)
|
||||
case strings.HasPrefix(profile.Key(), makeProfileKey(stampNamespace, "")):
|
||||
updateActiveStampProfile(profile)
|
||||
}
|
||||
}
|
||||
specialProfileLock.Unlock()
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue