Initial commit after restructure
This commit is contained in:
76
profiles/framework.go
Normal file
76
profiles/framework.go
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package profiles
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/Safing/safing-core/log"
|
||||
)
|
||||
|
||||
type Framework struct {
|
||||
// go hirarchy up
|
||||
FindParent uint8 `json:",omitempty bson:",omitempty"`
|
||||
// get path from parent, amount of levels to go up the tree (1 means parent, 2 means parent of parents, and so on)
|
||||
MergeWithParent bool `json:",omitempty bson:",omitempty"`
|
||||
// instead of getting the path of the parent, merge with it by presenting connections as if they were from that parent
|
||||
|
||||
// go hirarchy down
|
||||
Find string `json:",omitempty bson:",omitempty"`
|
||||
// Regular expression for finding path elements
|
||||
Build string `json:",omitempty bson:",omitempty"`
|
||||
// Path definitions for building path
|
||||
Virtual bool `json:",omitempty bson:",omitempty"`
|
||||
// Treat resulting path as virtual, do not check if valid
|
||||
}
|
||||
|
||||
func (f *Framework) GetNewPath(command string, cwd string) (string, error) {
|
||||
// "/usr/bin/python script"
|
||||
// to
|
||||
// "/path/to/script"
|
||||
regex, err := regexp.Compile(f.Find)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("profiles(framework): failed to compile framework regex: %s", err)
|
||||
}
|
||||
matched := regex.FindAllStringSubmatch(command, -1)
|
||||
if len(matched) == 0 || len(matched[0]) < 2 {
|
||||
return "", fmt.Errorf("profiles(framework): regex \"%s\" for constructing path did not match command \"%s\"", f.Find, command)
|
||||
}
|
||||
|
||||
var lastError error
|
||||
var buildPath string
|
||||
for _, buildPath = range strings.Split(f.Build, "|") {
|
||||
|
||||
buildPath = strings.Replace(buildPath, "{CWD}", cwd, -1)
|
||||
for i := 1; i < len(matched[0]); i++ {
|
||||
buildPath = strings.Replace(buildPath, fmt.Sprintf("{%d}", i), matched[0][i], -1)
|
||||
}
|
||||
|
||||
buildPath = filepath.Clean(buildPath)
|
||||
|
||||
if !f.Virtual {
|
||||
if !strings.HasPrefix(buildPath, "~/") && !filepath.IsAbs(buildPath) {
|
||||
lastError = fmt.Errorf("constructed path \"%s\" from framework is not absolute", buildPath)
|
||||
continue
|
||||
}
|
||||
if _, err := os.Stat(buildPath); os.IsNotExist(err) {
|
||||
lastError = fmt.Errorf("constructed path \"%s\" does not exist", buildPath)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
lastError = nil
|
||||
break
|
||||
|
||||
}
|
||||
|
||||
if lastError != nil {
|
||||
return "", fmt.Errorf("profiles(framework): failed to construct valid path, last error: %s", lastError)
|
||||
}
|
||||
log.Tracef("profiles(framework): transformed \"%s\" (%s) to \"%s\"", command, cwd, buildPath)
|
||||
return buildPath, nil
|
||||
}
|
||||
30
profiles/framework_test.go
Normal file
30
profiles/framework_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package profiles
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testGetNewPath(t *testing.T, f *Framework, command, cwd, expect string) {
|
||||
newPath, err := f.GetNewPath(command, cwd)
|
||||
if err != nil {
|
||||
t.Errorf("GetNewPath failed: %s", err)
|
||||
}
|
||||
if newPath != expect {
|
||||
t.Errorf("GetNewPath return unexpected result: got %s, expected %s", newPath, expect)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFramework(t *testing.T) {
|
||||
f1 := &Framework{
|
||||
Find: "([^ ]+)$",
|
||||
Build: "{CWD}/{1}",
|
||||
}
|
||||
testGetNewPath(t, f1, "/usr/bin/python bash", "/bin", "/bin/bash")
|
||||
f2 := &Framework{
|
||||
Find: "([^ ]+)$",
|
||||
Build: "{1}|{CWD}/{1}",
|
||||
}
|
||||
testGetNewPath(t, f2, "/usr/bin/python /bin/bash", "/tmp", "/bin/bash")
|
||||
}
|
||||
265
profiles/profile.go
Normal file
265
profiles/profile.go
Normal file
@@ -0,0 +1,265 @@
|
||||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package profiles
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
|
||||
datastore "github.com/ipfs/go-datastore"
|
||||
dsq "github.com/ipfs/go-datastore/query"
|
||||
|
||||
"github.com/Safing/safing-core/database"
|
||||
"github.com/Safing/safing-core/intel"
|
||||
"github.com/Safing/safing-core/log"
|
||||
)
|
||||
|
||||
// Profile is used to predefine a security profile for applications.
|
||||
type Profile struct {
|
||||
database.Base
|
||||
Name string
|
||||
Path string
|
||||
Description string `json:",omitempty bson:",omitempty"`
|
||||
Icon string `json:",omitempty bson:",omitempty"`
|
||||
// Icon is a path to the icon and is either prefixed "f:" for filepath, "d:" for database cache path or "c:"/"a:" for a the icon key to fetch it from a company / authoritative node and cache it in its own cache.
|
||||
|
||||
// TODO: Think more about using one profile for multiple paths
|
||||
// Refer string `json:",omitempty bson:",omitempty"`
|
||||
|
||||
// If a Profile is declared as a Framework (i.e. an Interpreter and the likes), then the real process must be found
|
||||
Framework *Framework `json:",omitempty bson:",omitempty"`
|
||||
// The format how to real process is to be found is yet to be defined.
|
||||
// Ideas:
|
||||
// - Regex for finding the executed script in the arguments, prepend working directory if path is not absolute
|
||||
// - Parent Process?
|
||||
// Use Cases:
|
||||
// - Interpreters (Python, Java, ...)
|
||||
// - Sandboxes (Flatpak, Snapd, Docker, ...)
|
||||
// - Subprocesses of main application process
|
||||
|
||||
SecurityLevel int8 `json:",omitempty bson:",omitempty"`
|
||||
// The mininum security level to apply to connections made with this profile
|
||||
Flags ProfileFlags
|
||||
|
||||
ClassificationBlacklist *intel.EntityClassification `json:",omitempty bson:",omitempty"`
|
||||
ClassificationWhitelist *intel.EntityClassification `json:",omitempty bson:",omitempty"`
|
||||
DomainWhitelistIsBlacklist bool `json:",omitempty bson:",omitempty"`
|
||||
DomainWhitelist []string `json:",omitempty bson:",omitempty"`
|
||||
|
||||
ConnectPorts []uint16 `json:",omitempty bson:",omitempty"`
|
||||
ListenPorts []uint16 `json:",omitempty bson:",omitempty"`
|
||||
|
||||
Default bool `json:",omitempty bson:",omitempty"`
|
||||
// This flag indicates that this profile is a default profile. If no profile is found for a process, the default profile with the longest matching prefix path is used.
|
||||
PromptUserToAdapt bool `json:",omitempty bson:",omitempty"`
|
||||
// This flag is only valid for default profiles and indicates that should this profile be used for a process, the user will be prompted to adapt it for the process and save a new profile.
|
||||
Authoritative bool `json:",omitempty bson:",omitempty"`
|
||||
// This flag indicates that this profile was loaded from an authoritative source - the Safing Community or the Company. Authoritative Profiles that have been modified can be reverted back to their original state.
|
||||
Locked bool `json:",omitempty bson:",omitempty"`
|
||||
// This flag indicates that this profile was locked by the company. This means that the profile may not be edited by the user. If an authoritative default profile is locked, it wins over non-authoritative profiles and the user will not be prompted to adapt the profile, should the PromptUserToAdapt flag be set.
|
||||
Modified bool `json:",omitempty bson:",omitempty"`
|
||||
// This flag indicates that this profile has been modified by the user. Non-modified authoritative profiles will be automatically overwritten with new versions.
|
||||
Orphaned bool `json:",omitempty bson:",omitempty"`
|
||||
// This flag indicates that the associated program (on path) does not exist (Either this entry was manually created, or the program has been uninstalled). Only valid for non-default profiles.
|
||||
|
||||
ApproxLastUsed int64 `json:",omitempty bson:",omitempty"`
|
||||
// When this Profile was approximately last used (for performance reasons not every single usage is saved)
|
||||
}
|
||||
|
||||
var profileModel *Profile // only use this as parameter for database.EnsureModel-like functions
|
||||
|
||||
func init() {
|
||||
database.RegisterModel(profileModel, func() database.Model { return new(Profile) })
|
||||
}
|
||||
|
||||
// Create saves Profile with the provided name in the Profiles namespace.
|
||||
func (m *Profile) Create() error {
|
||||
name := hex.EncodeToString([]byte(m.Path))
|
||||
if m.Default {
|
||||
name = "d-" + name
|
||||
}
|
||||
return m.CreateObject(&database.Profiles, name, m)
|
||||
}
|
||||
|
||||
// CreateInNamespace saves Profile with the provided name in the provided namespace.
|
||||
func (m *Profile) CreateInNamespace(namespace *datastore.Key) error {
|
||||
name := hex.EncodeToString([]byte(m.Path))
|
||||
if m.Default {
|
||||
name = "d-" + name
|
||||
}
|
||||
return m.CreateObject(namespace, name, m)
|
||||
}
|
||||
|
||||
// CreateInDist saves Profile with the (hash of the) path as the name in the Dist namespace.
|
||||
func (m *Profile) CreateInDist() error {
|
||||
return m.CreateInNamespace(&database.DistProfiles)
|
||||
}
|
||||
|
||||
// CreateInCompany saves Profile with the (hash of the) path as the name in the Company namespace.
|
||||
func (m *Profile) CreateInCompany() error {
|
||||
return m.CreateInNamespace(&database.CompanyProfiles)
|
||||
}
|
||||
|
||||
// Save saves Profile.
|
||||
func (m *Profile) Save() error {
|
||||
return m.SaveObject(m)
|
||||
}
|
||||
|
||||
// String returns a string representation of Profile.
|
||||
func (m *Profile) String() string {
|
||||
if m.Default {
|
||||
return "[D] " + m.Name
|
||||
}
|
||||
return m.Name
|
||||
}
|
||||
|
||||
// GetProfile fetches Profile with the provided name from the default namespace.
|
||||
func GetProfile(name string) (*Profile, error) {
|
||||
return GetProfileFromNamespace(&database.Profiles, name)
|
||||
}
|
||||
|
||||
// GetProfileFromNamespace fetches Profile with the provided name from the provided namespace.
|
||||
func GetProfileFromNamespace(namespace *datastore.Key, name string) (*Profile, error) {
|
||||
object, err := database.GetAndEnsureModel(namespace, name, profileModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
model, ok := object.(*Profile)
|
||||
if !ok {
|
||||
return nil, database.NewMismatchError(object, profileModel)
|
||||
}
|
||||
return model, nil
|
||||
}
|
||||
|
||||
// GetActiveProfileByPath fetches Profile with the (hash of the) path as the name from the default namespace.
|
||||
func GetActiveProfileByPath(path string) (*Profile, error) {
|
||||
return GetProfileFromNamespace(&database.Profiles, hex.EncodeToString([]byte(path)))
|
||||
// TODO: check for locked authoritative default profiles
|
||||
}
|
||||
|
||||
// FindProfileByPath looks for a Profile first in the Company namespace and then in the Dist namespace. Should no Profile be available it searches for a Default Profile.
|
||||
func FindProfileByPath(path, homeDir string) (profile *Profile, err error) {
|
||||
name := hex.EncodeToString([]byte(path))
|
||||
var homeName string
|
||||
slashedHomeDir := strings.TrimRight(homeDir, "/") + "/"
|
||||
if homeDir != "" && strings.HasPrefix(path, slashedHomeDir) {
|
||||
homeName = hex.EncodeToString([]byte("~/" + path[len(slashedHomeDir):]))
|
||||
}
|
||||
|
||||
// check for available company profiles
|
||||
profile, err = GetProfileFromNamespace(&database.CompanyProfiles, name)
|
||||
if err != database.ErrNotFound {
|
||||
if err == nil {
|
||||
return profile.Activate()
|
||||
}
|
||||
return
|
||||
}
|
||||
if homeName != "" {
|
||||
profile, err = GetProfileFromNamespace(&database.CompanyProfiles, homeName)
|
||||
if err != database.ErrNotFound {
|
||||
if err == nil {
|
||||
return profile.Activate()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// check for available dist profiles
|
||||
profile, err = GetProfileFromNamespace(&database.DistProfiles, name)
|
||||
if err != database.ErrNotFound {
|
||||
if err == nil {
|
||||
return profile.Activate()
|
||||
}
|
||||
return
|
||||
}
|
||||
if homeName != "" {
|
||||
profile, err = GetProfileFromNamespace(&database.DistProfiles, homeName)
|
||||
if err != database.ErrNotFound {
|
||||
if err == nil {
|
||||
return profile.Activate()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// search for best-matching default profile
|
||||
err = nil
|
||||
profile, _ = SearchForDefaultProfile(name, homeName, len(slashedHomeDir)-2, &database.Profiles)
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Profile) Activate() (*Profile, error) {
|
||||
return m, m.Create()
|
||||
}
|
||||
|
||||
func SearchForDefaultProfile(matchKey, matchHomeKey string, addHomeDirLen int, namespace *datastore.Key) (*Profile, int) {
|
||||
|
||||
// log.Tracef("profiles: searching for default profile with %s", matchKey)
|
||||
|
||||
query := dsq.Query{
|
||||
Prefix: namespace.ChildString("Profile:d-").String(),
|
||||
}
|
||||
|
||||
// filter := LongestMatch{
|
||||
// Offset: len(query.Prefix),
|
||||
// Longest: 0,
|
||||
// Match: hex.EncodeToString([]byte(path)),
|
||||
// }
|
||||
// query.Filters = []dsq.Filter{
|
||||
// filter,
|
||||
// }
|
||||
|
||||
prefixOffset := len(query.Prefix)
|
||||
longest := 0
|
||||
var longestMatch interface{}
|
||||
|
||||
currentLongestIsHomeBased := false
|
||||
currentLength := 0
|
||||
|
||||
iterator, err := database.Query(query)
|
||||
if err != nil {
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
for entry, ok := iterator.NextSync(); ok; entry, ok = iterator.NextSync() {
|
||||
// log.Tracef("profiles: checking %s for default profile", entry.Key)
|
||||
// TODO: prioritize locked profiles
|
||||
prefix := entry.Key[prefixOffset:]
|
||||
// skip directly if current longest match is longer than the key
|
||||
// log.Tracef("profiles: comparing %s to %s", matchKey, prefix)
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(matchKey, prefix):
|
||||
currentLength = len(prefix)
|
||||
currentLongestIsHomeBased = false
|
||||
case strings.HasPrefix(matchHomeKey, prefix):
|
||||
currentLength = len(prefix) + addHomeDirLen
|
||||
currentLongestIsHomeBased = true
|
||||
default:
|
||||
continue
|
||||
}
|
||||
// longest wins, if a root-based and home-based tie, root-based wins.
|
||||
if currentLength > longest || (currentLongestIsHomeBased && currentLength == longest) {
|
||||
longest = currentLength
|
||||
longestMatch = entry.Value
|
||||
// log.Tracef("profiles: found new longest (%d) default profile match: %s", currentLength, entry.Key)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if longestMatch == nil {
|
||||
return nil, 0
|
||||
}
|
||||
matched, ok := longestMatch.(database.Model)
|
||||
if !ok {
|
||||
log.Warningf("profiles: matched default profile is not of type database.Model")
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
profile, ok := database.SilentEnsureModel(matched, profileModel).(*Profile)
|
||||
if !ok {
|
||||
log.Warningf("profiles: matched default profile is not of type *Profile")
|
||||
return nil, 0
|
||||
}
|
||||
return profile, longest
|
||||
}
|
||||
115
profiles/profileflags.go
Normal file
115
profiles/profileflags.go
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package profiles
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ProfileFlags are used to quickly add common attributes to profiles
|
||||
type ProfileFlags []int8
|
||||
|
||||
const (
|
||||
// Who?
|
||||
// System apps must be run by system user, else deny
|
||||
System int8 = iota + 1
|
||||
// Admin apps must be run by user with admin privileges, else deny
|
||||
Admin
|
||||
// User apps must be run by user (identified by having an active safing UI), else deny
|
||||
User
|
||||
|
||||
// Where?
|
||||
// Internet apps may connect to the Internet, if unset, all connections to the Internet are denied
|
||||
Internet
|
||||
// LocalNet apps may connect to the local network (i.e. private IP address spaces), if unset, all connections to the local network are denied
|
||||
LocalNet
|
||||
|
||||
// How?
|
||||
// Strict apps may only connect to domains that are related to themselves
|
||||
Strict
|
||||
// Gateway apps will connect to user-defined servers
|
||||
Gateway
|
||||
// Browser apps connect to multitudes of different servers and require special handling
|
||||
Browser
|
||||
// Directconnect apps may connect to any IP without dns association (e.g. P2P apps, network analysis tools)
|
||||
Directconnect
|
||||
// Service apps may accept incoming connections
|
||||
Service
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrProfileFlagsParseFailed is returned if a an invalid flag is encountered while parsing
|
||||
ErrProfileFlagsParseFailed = errors.New("profiles: failed to parse flags")
|
||||
|
||||
sortedFlags = &ProfileFlags{
|
||||
System,
|
||||
Admin,
|
||||
User,
|
||||
Internet,
|
||||
LocalNet,
|
||||
Strict,
|
||||
Gateway,
|
||||
Service,
|
||||
Directconnect,
|
||||
Browser,
|
||||
}
|
||||
|
||||
flagIDs = map[string]int8{
|
||||
"System": System,
|
||||
"Admin": Admin,
|
||||
"User": User,
|
||||
"Internet": Internet,
|
||||
"LocalNet": LocalNet,
|
||||
"Strict": Strict,
|
||||
"Gateway": Gateway,
|
||||
"Service": Service,
|
||||
"Directconnect": Directconnect,
|
||||
"Browser": Browser,
|
||||
}
|
||||
|
||||
flagNames = map[int8]string{
|
||||
System: "System",
|
||||
Admin: "Admin",
|
||||
User: "User",
|
||||
Internet: "Internet",
|
||||
LocalNet: "LocalNet",
|
||||
Strict: "Strict",
|
||||
Gateway: "Gateway",
|
||||
Service: "Service",
|
||||
Directconnect: "Directconnect",
|
||||
Browser: "Browser",
|
||||
}
|
||||
)
|
||||
|
||||
// FlagsFromNames creates ProfileFlags from a comma seperated list of flagnames (e.g. "System,Strict,Secure")
|
||||
func FlagsFromNames(words []string) (*ProfileFlags, error) {
|
||||
var flags ProfileFlags
|
||||
for _, entry := range words {
|
||||
flag, ok := flagIDs[entry]
|
||||
if !ok {
|
||||
return nil, ErrProfileFlagsParseFailed
|
||||
}
|
||||
flags = append(flags, flag)
|
||||
}
|
||||
return &flags, nil
|
||||
}
|
||||
|
||||
// Has checks if a ProfileFlags object has a flag
|
||||
func (pf *ProfileFlags) Has(searchFlag int8) bool {
|
||||
for _, flag := range *pf {
|
||||
if flag == searchFlag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// String return a string representation of ProfileFlags
|
||||
func (pf *ProfileFlags) String() string {
|
||||
var namedFlags []string
|
||||
for _, flag := range *pf {
|
||||
namedFlags = append(namedFlags, flagNames[flag])
|
||||
}
|
||||
return strings.Join(namedFlags, ",")
|
||||
}
|
||||
65
profiles/profileflags_test.go
Normal file
65
profiles/profileflags_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package profiles
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProfileFlags(t *testing.T) {
|
||||
|
||||
// check if SYSTEM is 1
|
||||
if System != 1 {
|
||||
t.Errorf("System ist first const and must be 1")
|
||||
}
|
||||
if Admin != 2 {
|
||||
t.Errorf("Admin ist second const and must be 2")
|
||||
}
|
||||
|
||||
// check if all IDs have a name
|
||||
for key, entry := range flagIDs {
|
||||
if _, ok := flagNames[entry]; !ok {
|
||||
t.Errorf("could not find entry for %s in flagNames", key)
|
||||
}
|
||||
}
|
||||
|
||||
// check if all names have an ID
|
||||
for key, entry := range flagNames {
|
||||
if _, ok := flagIDs[entry]; !ok {
|
||||
t.Errorf("could not find entry for %d in flagNames", key)
|
||||
}
|
||||
}
|
||||
|
||||
// check Has
|
||||
emptyFlags := ProfileFlags{}
|
||||
for flag, name := range flagNames {
|
||||
if !sortedFlags.Has(flag) {
|
||||
t.Errorf("sortedFlags should have flag %s (%d)", name, flag)
|
||||
}
|
||||
if emptyFlags.Has(flag) {
|
||||
t.Errorf("emptyFlags should not have flag %s (%d)", name, flag)
|
||||
}
|
||||
}
|
||||
|
||||
// check ProfileFlags creation from strings
|
||||
var allFlagStrings []string
|
||||
for _, flag := range *sortedFlags {
|
||||
allFlagStrings = append(allFlagStrings, flagNames[flag])
|
||||
}
|
||||
newFlags, err := FlagsFromNames(allFlagStrings)
|
||||
if err != nil {
|
||||
t.Errorf("error while parsing flags: %s", err)
|
||||
}
|
||||
if newFlags.String() != sortedFlags.String() {
|
||||
t.Errorf("parsed flags are not correct (or tests have not been updated to reflect the right number), expected %v, got %v", *sortedFlags, *newFlags)
|
||||
}
|
||||
|
||||
// check ProfileFlags Stringer
|
||||
flagString := newFlags.String()
|
||||
check := strings.Join(allFlagStrings, ",")
|
||||
if flagString != check {
|
||||
t.Errorf("flag string is not correct, expected %s, got %s", check, flagString)
|
||||
}
|
||||
|
||||
}
|
||||
263
profiles/sampledata.go
Normal file
263
profiles/sampledata.go
Normal file
@@ -0,0 +1,263 @@
|
||||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package profiles
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
ds "github.com/ipfs/go-datastore"
|
||||
|
||||
"github.com/Safing/safing-core/database"
|
||||
"github.com/Safing/safing-core/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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user