Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmds/portmaster-core/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
_ "github.com/safing/portbase/modules/subsystems"
_ "github.com/safing/portmaster/core"
_ "github.com/safing/portmaster/firewall"
_ "github.com/safing/portmaster/firewall/inspection/encryption"
_ "github.com/safing/portmaster/firewall/inspection/portscan"
_ "github.com/safing/portmaster/nameserver"
_ "github.com/safing/portmaster/ui"
_ "github.com/safing/spn/captain"
Expand Down
50 changes: 50 additions & 0 deletions firewall/inspection/encryption/detect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package encryption

import (
"github.com/safing/portmaster/firewall/inspection"
"github.com/safing/portmaster/network"
"github.com/safing/portmaster/network/packet"
)

// Detector detects if a connection is encrypted.
type Detector struct{}

// Name implements the inspection interface.
func (d *Detector) Name() string {
return "Encryption Detection"
}

// Inspect implements the inspection interface.
func (d *Detector) Inspect(conn *network.Connection, pkt packet.Packet) (pktVerdict network.Verdict, proceed bool, err error) {
if !conn.Inbound {
switch conn.Entity.Port {
case 443, 465, 993, 995:
conn.Encrypted = true
conn.SaveWhenFinished()
}
}

return network.VerdictUndecided, false, nil
}

// Destroy implements the destroy interface.
func (d *Detector) Destroy() error {
return nil
}

// DetectorFactory is a primitive detection method that runs within the factory only.
func DetectorFactory(conn *network.Connection, pkt packet.Packet) (network.Inspector, error) {
return &Detector{}, nil
}

// Register registers the encryption detection inspector with the inspection framework.
func init() {
err := inspection.RegisterInspector(&inspection.Registration{
Name: "Encryption Detection",
Order: 0,
Factory: DetectorFactory,
})
if err != nil {
panic(err)
}
}
151 changes: 82 additions & 69 deletions firewall/inspection/inspection.go
Original file line number Diff line number Diff line change
@@ -1,102 +1,115 @@
package inspection

import (
"errors"
"sort"
"sync"

"github.com/safing/portbase/log"
"github.com/safing/portmaster/network"
"github.com/safing/portmaster/network/packet"
)

//nolint:golint,stylecheck // FIXME
const (
DO_NOTHING uint8 = iota
BLOCK_PACKET
DROP_PACKET
BLOCK_CONN
DROP_CONN
STOP_INSPECTING
)
// Registration holds information about and the factory of a registered inspector.
type Registration struct {
// Name of the Inspector
Name string

// Order defines the priority in which the inspector should run. Decrease for higher priority, increase for lower priority. Leave at 0 for no preference.
Order int

// Factory creates a new inspector. It may also return a nil inspector, which means that no inspection is desired.
// Any processing on the packet should only occur on the first call of Inspect. After creating a new Inspector, Inspect is is called with the same connection and packet for actual processing.
Factory func(conn *network.Connection, pkt packet.Packet) (network.Inspector, error)
}

// Registry is a sortable []*Registration wrapper.
type Registry []*Registration

type inspectorFn func(*network.Connection, packet.Packet) uint8
func (r Registry) Len() int { return len(r) }
func (r Registry) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r Registry) Less(i, j int) bool { return r[i].Order < r[j].Order }

var (
inspectors []inspectorFn
inspectorNames []string
inspectVerdicts []network.Verdict
inspectorsLock sync.Mutex
inspectorRegistry []*Registration
inspectorRegistryLock sync.Mutex
)

// RegisterInspector registers a traffic inspector.
func RegisterInspector(name string, inspector inspectorFn, inspectVerdict network.Verdict) (index int) {
inspectorsLock.Lock()
defer inspectorsLock.Unlock()
index = len(inspectors)
inspectors = append(inspectors, inspector)
inspectorNames = append(inspectorNames, name)
inspectVerdicts = append(inspectVerdicts, inspectVerdict)
return
}
func RegisterInspector(new *Registration) error {
inspectorRegistryLock.Lock()
defer inspectorRegistryLock.Unlock()

// RunInspectors runs all the applicable inspectors on the given packet.
func RunInspectors(conn *network.Connection, pkt packet.Packet) (network.Verdict, bool) {
// inspectorsLock.Lock()
// defer inspectorsLock.Unlock()

activeInspectors := conn.GetActiveInspectors()
if activeInspectors == nil {
activeInspectors = make([]bool, len(inspectors))
conn.SetActiveInspectors(activeInspectors)
if new.Factory == nil {
return errors.New("missing inspector factory")
}

inspectorData := conn.GetInspectorData()
if inspectorData == nil {
inspectorData = make(map[uint8]interface{})
conn.SetInspectorData(inspectorData)
// check if name exists
for _, r := range inspectorRegistry {
if new.Name == r.Name {
return errors.New("already registered")
}
}

continueInspection := false
verdict := network.VerdictUndecided
// append to list
inspectorRegistry = append(inspectorRegistry, new)

for key, skip := range activeInspectors {
// sort
sort.Stable(Registry(inspectorRegistry))

if skip {
continue
return nil
}

// InitializeInspectors initializes all applicable inspectors for the connection.
func InitializeInspectors(conn *network.Connection, pkt packet.Packet) {
inspectorRegistryLock.Lock()
defer inspectorRegistryLock.Unlock()

connInspectors := make([]network.Inspector, 0, len(inspectorRegistry))
for _, r := range inspectorRegistry {
inspector, err := r.Factory(conn, pkt)
switch {
case err != nil:
log.Tracer(pkt.Ctx()).Warningf("failed to initialize inspector %s: %v", r.Name, err)
case inspector != nil:
connInspectors = append(connInspectors, inspector)
}
}

conn.SetInspectors(connInspectors)
}

// check if the current verdict is already past the inspection criteria.
if conn.Verdict > inspectVerdicts[key] {
activeInspectors[key] = true
// RunInspectors runs all the applicable inspectors on the given packet of the connection. It returns the first error received by an inspector.
func RunInspectors(conn *network.Connection, pkt packet.Packet) (pktVerdict network.Verdict, continueInspection bool) {
connInspectors := conn.GetInspectors()
for i, inspector := range connInspectors {
// check if slot is active
if inspector == nil {
continue
}

action := inspectors[key](conn, pkt) // Actually run inspector
switch action {
case DO_NOTHING:
if verdict < network.VerdictAccept {
verdict = network.VerdictAccept
}
continueInspection = true
case BLOCK_PACKET:
if verdict < network.VerdictBlock {
verdict = network.VerdictBlock
}
continueInspection = true
case DROP_PACKET:
verdict = network.VerdictDrop
// run inspector
inspectorPktVerdict, proceed, err := inspector.Inspect(conn, pkt)
if err != nil {
log.Tracer(pkt.Ctx()).Warningf("inspector %s failed: %s", inspector.Name(), err)
}
// merge
if inspectorPktVerdict > pktVerdict {
pktVerdict = inspectorPktVerdict
}
if proceed {
continueInspection = true
case BLOCK_CONN:
conn.SetVerdict(network.VerdictBlock, "", nil)
verdict = conn.Verdict
activeInspectors[key] = true
case DROP_CONN:
conn.SetVerdict(network.VerdictDrop, "", nil)
verdict = conn.Verdict
activeInspectors[key] = true
case STOP_INSPECTING:
activeInspectors[key] = true
}

// destroy if finished or failed
if !proceed || err != nil {
err = inspector.Destroy()
if err != nil {
log.Tracer(pkt.Ctx()).Debugf("inspector %s failed to destroy: %s", inspector.Name(), err)
}
connInspectors[i] = nil
}
}

return verdict, continueInspection
return pktVerdict, continueInspection
}
Loading