Work on pm restructure

This commit is contained in:
Daniel 2018-12-03 20:02:03 +01:00
parent 62b1c03edc
commit 8fb21fd900
23 changed files with 487 additions and 326 deletions

View file

@ -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.

View file

@ -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
View 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()
}
}

View file

@ -2,8 +2,7 @@ package profile
// Platform identifiers
const (
PlatformAny = "any"
PlatformLinux = "lin"
PlatformWindows = "win"
PlatformMac = "mac"
PlatformLinux = "linux"
PlatformWindows = "windows"
PlatformMac = "macos"
)

View file

@ -2,5 +2,5 @@ package profile
// OS Identifier
const (
osIdentifier = "mac"
osIdentifier = PlatformMac
)

View file

@ -2,5 +2,5 @@ package profile
// OS Identifier
const (
osIdentifier = "lin"
osIdentifier = PlatformLinux
)

View file

@ -12,8 +12,9 @@ import (
// Namespaces
const (
UserNamespace = "user"
StampNamespace = "stamp"
userNamespace = "user"
stampNamespace = "stamp"
specialNamespace = "special"
)
var (

82
profile/defaults.go Normal file
View 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,
},
},
},
}
}

View file

@ -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 {

View 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, "/")
}

View 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")
}

View file

@ -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)
}

View file

@ -1,4 +1,4 @@
package matcher
package matching
import (
"github.com/Safing/portbase/database"

3
profile/module.go Normal file
View file

@ -0,0 +1,3 @@
package profile
.

View file

@ -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
}

View file

@ -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)

View file

@ -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()
// }
// }
//
// }

View file

@ -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
View 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()
}
}