Complete first alpha version
This commit is contained in:
@@ -45,7 +45,7 @@ func (p *Process) Save() {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
if p.DatabaseKey() == "" {
|
||||
if !p.KeyIsSet() {
|
||||
p.SetKey(fmt.Sprintf("network:tree/%d", p.Pid))
|
||||
p.CreateMeta()
|
||||
}
|
||||
@@ -61,21 +61,22 @@ func (p *Process) Save() {
|
||||
}
|
||||
|
||||
if dbControllerFlag.IsSet() {
|
||||
dbController.PushUpdate(p)
|
||||
go dbController.PushUpdate(p)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes a process from the storage and propagates the change.
|
||||
func (p *Process) Delete() {
|
||||
processesLock.Lock()
|
||||
defer processesLock.Unlock()
|
||||
delete(processes, p.Pid)
|
||||
p.Lock()
|
||||
defer p.Lock()
|
||||
p.Meta().Delete()
|
||||
|
||||
processesLock.Lock()
|
||||
delete(processes, p.Pid)
|
||||
processesLock.Unlock()
|
||||
|
||||
p.Meta().Delete()
|
||||
if dbControllerFlag.IsSet() {
|
||||
dbController.PushUpdate(p)
|
||||
go dbController.PushUpdate(p)
|
||||
}
|
||||
|
||||
profile.DeactivateProfileSet(p.profileSet)
|
||||
@@ -88,9 +89,11 @@ func CleanProcessStorage(thresholdDuration time.Duration) {
|
||||
|
||||
threshold := time.Now().Add(-thresholdDuration).Unix()
|
||||
for _, p := range processes {
|
||||
p.Lock()
|
||||
if p.FirstConnectionEstablished < threshold && p.ConnectionCount == 0 {
|
||||
p.Delete()
|
||||
go p.Delete()
|
||||
}
|
||||
p.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,14 @@ import (
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/Safing/portbase/log"
|
||||
"github.com/Safing/portmaster/network/packet"
|
||||
)
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrConnectionNotFound = errors.New("could not find connection")
|
||||
ErrProcessNotFound = errors.New("could not find process")
|
||||
ErrConnectionNotFound = errors.New("could not find connection in system state tables")
|
||||
ErrProcessNotFound = errors.New("could not find process in system state tables")
|
||||
)
|
||||
|
||||
// GetPidByPacket returns the pid of the owner of the packet.
|
||||
@@ -57,18 +58,23 @@ func GetProcessByPacket(pkt packet.Packet) (process *Process, direction bool, er
|
||||
|
||||
var pid int
|
||||
pid, direction, err = GetPidByPacket(pkt)
|
||||
if pid < 0 {
|
||||
return nil, direction, ErrConnectionNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return nil, direction, err
|
||||
}
|
||||
if pid < 0 {
|
||||
return nil, direction, ErrConnectionNotFound
|
||||
}
|
||||
|
||||
process, err = GetOrFindProcess(pid)
|
||||
if err != nil {
|
||||
return nil, direction, err
|
||||
}
|
||||
|
||||
err = process.FindProfiles()
|
||||
if err != nil {
|
||||
log.Errorf("failed to find profiles for process %s: %s", process.String(), err)
|
||||
}
|
||||
|
||||
return process, direction, nil
|
||||
|
||||
}
|
||||
@@ -113,6 +119,11 @@ func GetProcessByEndpoints(localIP net.IP, localPort uint16, remoteIP net.IP, re
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = process.FindProfiles()
|
||||
if err != nil {
|
||||
log.Errorf("failed to find profiles for process %s: %s", process.String(), err)
|
||||
}
|
||||
|
||||
return process, nil
|
||||
|
||||
}
|
||||
|
||||
@@ -1,52 +1,59 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Safing/portbase/database"
|
||||
"github.com/Safing/portbase/database/query"
|
||||
"github.com/Safing/portbase/log"
|
||||
"github.com/Safing/portmaster/profile"
|
||||
"github.com/Safing/portmaster/profile/index"
|
||||
)
|
||||
|
||||
var (
|
||||
profileDB = database.NewInterface(nil)
|
||||
)
|
||||
|
||||
// FindProfiles finds and assigns a profile set to the process.
|
||||
func (p *Process) FindProfiles() error {
|
||||
|
||||
// Get fingerprints of process
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
// 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)
|
||||
// only find profiles if not already done.
|
||||
if p.profileSet != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
// User Profile
|
||||
it, err := profileDB.Query(query.New(profile.MakeProfileKey(profile.UserNamespace, "")).Where(query.Where("LinkedPath", query.SameAs, p.Path)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var userProfile *profile.Profile
|
||||
for r := range it.Next {
|
||||
it.Cancel()
|
||||
userProfile, err = profile.EnsureProfile(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
if it.Err() != nil {
|
||||
return it.Err()
|
||||
}
|
||||
|
||||
prof := selectProfile(p, possibleProfiles)
|
||||
if prof == nil {
|
||||
// create new profile if it does not exist.
|
||||
if userProfile == 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()
|
||||
userProfile = profile.New()
|
||||
userProfile.Name = p.ExecName
|
||||
userProfile.LinkedPath = p.Path
|
||||
}
|
||||
|
||||
if userProfile.MarkUsed() {
|
||||
userProfile.Save(profile.UserNamespace)
|
||||
}
|
||||
|
||||
// Stamp
|
||||
// Find/Re-evaluate Stamp profile
|
||||
// 1. check linked stamp profile
|
||||
// 2. if last check is was more than a week ago, fetch from stamp:
|
||||
@@ -56,13 +63,9 @@ func (p *Process) FindProfiles() error {
|
||||
// 6. link stamp profile to user profile
|
||||
// FIXME: implement!
|
||||
|
||||
if prof.MarkUsed() {
|
||||
prof.Save()
|
||||
}
|
||||
|
||||
p.UserProfileKey = prof.Key()
|
||||
p.profileSet = profile.NewSet(prof, nil)
|
||||
p.Save()
|
||||
p.UserProfileKey = userProfile.Key()
|
||||
p.profileSet = profile.NewSet(userProfile, nil)
|
||||
go p.Save()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -13,14 +13,19 @@ const (
|
||||
NoProcess
|
||||
)
|
||||
|
||||
func GetPidOfConnection(localIP *net.IP, localPort uint16, protocol uint8) (pid int, status uint8) {
|
||||
var (
|
||||
waitTime = 15 * time.Millisecond
|
||||
)
|
||||
|
||||
// GetPidOfConnection returns the PID of the given connection.
|
||||
func GetPidOfConnection(localIP net.IP, localPort uint16, protocol uint8) (pid int, status uint8) {
|
||||
uid, inode, ok := getConnectionSocket(localIP, localPort, protocol)
|
||||
if !ok {
|
||||
uid, inode, ok = getListeningSocket(localIP, localPort, protocol)
|
||||
for i := 0; i < 3 && !ok; i++ {
|
||||
// give kernel some time, then try again
|
||||
// log.Tracef("process: giving kernel some time to think")
|
||||
time.Sleep(15 * time.Millisecond)
|
||||
time.Sleep(waitTime)
|
||||
uid, inode, ok = getConnectionSocket(localIP, localPort, protocol)
|
||||
if !ok {
|
||||
uid, inode, ok = getListeningSocket(localIP, localPort, protocol)
|
||||
@@ -30,27 +35,48 @@ func GetPidOfConnection(localIP *net.IP, localPort uint16, protocol uint8) (pid
|
||||
return -1, NoSocket
|
||||
}
|
||||
}
|
||||
|
||||
pid, ok = GetPidOfInode(uid, inode)
|
||||
for i := 0; i < 3 && !ok; i++ {
|
||||
// give kernel some time, then try again
|
||||
// log.Tracef("process: giving kernel some time to think")
|
||||
time.Sleep(15 * time.Millisecond)
|
||||
time.Sleep(waitTime)
|
||||
pid, ok = GetPidOfInode(uid, inode)
|
||||
}
|
||||
if !ok {
|
||||
return -1, NoProcess
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func GetPidOfIncomingConnection(localIP *net.IP, localPort uint16, protocol uint8) (pid int, status uint8) {
|
||||
// GetPidOfConnection returns the PID of the given incoming connection.
|
||||
func GetPidOfIncomingConnection(localIP net.IP, localPort uint16, protocol uint8) (pid int, status uint8) {
|
||||
uid, inode, ok := getListeningSocket(localIP, localPort, protocol)
|
||||
if !ok {
|
||||
return -1, NoSocket
|
||||
// for TCP4 and UDP4, also try TCP6 and UDP6, as linux sometimes treats them as a single dual socket, and shows the IPv6 version.
|
||||
switch protocol {
|
||||
case TCP4:
|
||||
uid, inode, ok = getListeningSocket(localIP, localPort, TCP6)
|
||||
case UDP4:
|
||||
uid, inode, ok = getListeningSocket(localIP, localPort, UDP6)
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return -1, NoSocket
|
||||
}
|
||||
}
|
||||
|
||||
pid, ok = GetPidOfInode(uid, inode)
|
||||
for i := 0; i < 3 && !ok; i++ {
|
||||
// give kernel some time, then try again
|
||||
// log.Tracef("process: giving kernel some time to think")
|
||||
time.Sleep(waitTime)
|
||||
pid, ok = GetPidOfInode(uid, inode)
|
||||
}
|
||||
if !ok {
|
||||
return -1, NoProcess
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -6,39 +6,39 @@ import (
|
||||
)
|
||||
|
||||
func GetTCP4PacketInfo(localIP net.IP, localPort uint16, remoteIP net.IP, remotePort uint16, pktDirection bool) (pid int, direction bool, err error) {
|
||||
return search(TCP4, localIP, localPort, direction)
|
||||
return search(TCP4, localIP, localPort, pktDirection)
|
||||
}
|
||||
|
||||
func GetTCP6PacketInfo(localIP net.IP, localPort uint16, remoteIP net.IP, remotePort uint16, pktDirection bool) (pid int, direction bool, err error) {
|
||||
return search(TCP6, localIP, localPort, direction)
|
||||
return search(TCP6, localIP, localPort, pktDirection)
|
||||
}
|
||||
|
||||
func GetUDP4PacketInfo(localIP net.IP, localPort uint16, remoteIP net.IP, remotePort uint16, pktDirection bool) (pid int, direction bool, err error) {
|
||||
return search(UDP4, localIP, localPort, direction)
|
||||
return search(UDP4, localIP, localPort, pktDirection)
|
||||
}
|
||||
|
||||
func GetUDP6PacketInfo(localIP net.IP, localPort uint16, remoteIP net.IP, remotePort uint16, pktDirection bool) (pid int, direction bool, err error) {
|
||||
return search(UDP6, localIP, localPort, direction)
|
||||
return search(UDP6, localIP, localPort, pktDirection)
|
||||
}
|
||||
|
||||
func search(protocol uint8, localIP net.IP, localPort uint16, pktDirection bool) (pid int, direction bool, err error) {
|
||||
|
||||
var status uint8
|
||||
if pktDirection {
|
||||
pid, status = GetPidOfIncomingConnection(&localIP, localPort, protocol)
|
||||
pid, status = GetPidOfIncomingConnection(localIP, localPort, protocol)
|
||||
if pid >= 0 {
|
||||
return pid, true, nil
|
||||
}
|
||||
// pid, status = GetPidOfConnection(&localIP, localPort, protocol)
|
||||
// pid, status = GetPidOfConnection(localIP, localPort, protocol)
|
||||
// if pid >= 0 {
|
||||
// return pid, false, nil
|
||||
// }
|
||||
} else {
|
||||
pid, status = GetPidOfConnection(&localIP, localPort, protocol)
|
||||
pid, status = GetPidOfConnection(localIP, localPort, protocol)
|
||||
if pid >= 0 {
|
||||
return pid, false, nil
|
||||
}
|
||||
// pid, status = GetPidOfIncomingConnection(&localIP, localPort, protocol)
|
||||
// pid, status = GetPidOfIncomingConnection(localIP, localPort, protocol)
|
||||
// if pid >= 0 {
|
||||
// return pid, true, nil
|
||||
// }
|
||||
|
||||
@@ -81,7 +81,7 @@ var (
|
||||
globalListeningUDP6 = make(map[uint16][]int)
|
||||
)
|
||||
|
||||
func getConnectionSocket(localIP *net.IP, localPort uint16, protocol uint8) (int, int, bool) {
|
||||
func getConnectionSocket(localIP net.IP, localPort uint16, protocol uint8) (int, int, bool) {
|
||||
// listeningSocketsLock.Lock()
|
||||
// defer listeningSocketsLock.Unlock()
|
||||
|
||||
@@ -98,10 +98,10 @@ func getConnectionSocket(localIP *net.IP, localPort uint16, protocol uint8) (int
|
||||
localIPHex = strings.ToUpper(hex.EncodeToString([]byte{localIPBytes[3], localIPBytes[2], localIPBytes[1], localIPBytes[0]}))
|
||||
case TCP6:
|
||||
procFile = TCP6Data
|
||||
localIPHex = hex.EncodeToString([]byte(*localIP))
|
||||
localIPHex = hex.EncodeToString([]byte(localIP))
|
||||
case UDP6:
|
||||
procFile = UDP6Data
|
||||
localIPHex = hex.EncodeToString([]byte(*localIP))
|
||||
localIPHex = hex.EncodeToString([]byte(localIP))
|
||||
}
|
||||
|
||||
localPortHex := fmt.Sprintf("%04X", localPort)
|
||||
@@ -162,38 +162,38 @@ func getConnectionSocket(localIP *net.IP, localPort uint16, protocol uint8) (int
|
||||
|
||||
}
|
||||
|
||||
func getListeningSocket(localIP *net.IP, localPort uint16, protocol uint8) (uid, inode int, ok bool) {
|
||||
func getListeningSocket(localIP net.IP, localPort uint16, protocol uint8) (uid, inode int, ok bool) {
|
||||
listeningSocketsLock.Lock()
|
||||
defer listeningSocketsLock.Unlock()
|
||||
|
||||
var addressListening *map[string][]int
|
||||
var globalListening *map[uint16][]int
|
||||
var addressListening map[string][]int
|
||||
var globalListening map[uint16][]int
|
||||
switch protocol {
|
||||
case TCP4:
|
||||
addressListening = &addressListeningTCP4
|
||||
globalListening = &globalListeningTCP4
|
||||
addressListening = addressListeningTCP4
|
||||
globalListening = globalListeningTCP4
|
||||
case UDP4:
|
||||
addressListening = &addressListeningUDP4
|
||||
globalListening = &globalListeningUDP4
|
||||
addressListening = addressListeningUDP4
|
||||
globalListening = globalListeningUDP4
|
||||
case TCP6:
|
||||
addressListening = &addressListeningTCP6
|
||||
globalListening = &globalListeningTCP6
|
||||
addressListening = addressListeningTCP6
|
||||
globalListening = globalListeningTCP6
|
||||
case UDP6:
|
||||
addressListening = &addressListeningUDP6
|
||||
globalListening = &globalListeningUDP6
|
||||
addressListening = addressListeningUDP6
|
||||
globalListening = globalListeningUDP6
|
||||
}
|
||||
|
||||
data, ok := (*addressListening)[fmt.Sprintf("%s:%d", localIP, localPort)]
|
||||
data, ok := addressListening[fmt.Sprintf("%s:%d", localIP, localPort)]
|
||||
if !ok {
|
||||
data, ok = (*globalListening)[localPort]
|
||||
data, ok = globalListening[localPort]
|
||||
}
|
||||
if ok {
|
||||
return data[0], data[1], true
|
||||
}
|
||||
updateListeners(protocol)
|
||||
data, ok = (*addressListening)[fmt.Sprintf("%s:%d", localIP, localPort)]
|
||||
data, ok = addressListening[fmt.Sprintf("%s:%d", localIP, localPort)]
|
||||
if !ok {
|
||||
data, ok = (*globalListening)[localPort]
|
||||
data, ok = globalListening[localPort]
|
||||
}
|
||||
if ok {
|
||||
return data[0], data[1], true
|
||||
@@ -206,7 +206,7 @@ func procDelimiter(c rune) bool {
|
||||
return unicode.IsSpace(c) || c == ':'
|
||||
}
|
||||
|
||||
func convertIPv4(data string) *net.IP {
|
||||
func convertIPv4(data string) net.IP {
|
||||
decoded, err := hex.DecodeString(data)
|
||||
if err != nil {
|
||||
log.Warningf("process: could not parse IPv4 %s: %s", data, err)
|
||||
@@ -217,10 +217,10 @@ func convertIPv4(data string) *net.IP {
|
||||
return nil
|
||||
}
|
||||
ip := net.IPv4(decoded[3], decoded[2], decoded[1], decoded[0])
|
||||
return &ip
|
||||
return ip
|
||||
}
|
||||
|
||||
func convertIPv6(data string) *net.IP {
|
||||
func convertIPv6(data string) net.IP {
|
||||
decoded, err := hex.DecodeString(data)
|
||||
if err != nil {
|
||||
log.Warningf("process: could not parse IPv6 %s: %s", data, err)
|
||||
@@ -231,7 +231,7 @@ func convertIPv6(data string) *net.IP {
|
||||
return nil
|
||||
}
|
||||
ip := net.IP(decoded)
|
||||
return &ip
|
||||
return ip
|
||||
}
|
||||
|
||||
func updateListeners(protocol uint8) {
|
||||
@@ -247,7 +247,7 @@ func updateListeners(protocol uint8) {
|
||||
}
|
||||
}
|
||||
|
||||
func getListenerMaps(procFile, zeroIP, socketStatusListening string, ipConverter func(string) *net.IP) (map[string][]int, map[uint16][]int) {
|
||||
func getListenerMaps(procFile, zeroIP, socketStatusListening string, ipConverter func(string) net.IP) (map[string][]int, map[uint16][]int) {
|
||||
addressListening := make(map[string][]int)
|
||||
globalListening := make(map[uint16][]int)
|
||||
|
||||
@@ -312,6 +312,7 @@ func getListenerMaps(procFile, zeroIP, socketStatusListening string, ipConverter
|
||||
return addressListening, globalListening
|
||||
}
|
||||
|
||||
// GetActiveConnectionIDs returns all connection IDs that are still marked as active by the OS.
|
||||
func GetActiveConnectionIDs() []string {
|
||||
var connections []string
|
||||
|
||||
@@ -323,7 +324,7 @@ func GetActiveConnectionIDs() []string {
|
||||
return connections
|
||||
}
|
||||
|
||||
func getConnectionIDsFromSource(source string, protocol uint16, ipConverter func(string) *net.IP) []string {
|
||||
func getConnectionIDsFromSource(source string, protocol uint16, ipConverter func(string) net.IP) []string {
|
||||
var connections []string
|
||||
|
||||
// open file
|
||||
|
||||
@@ -22,14 +22,14 @@ func TestSockets(t *testing.T) {
|
||||
t.Logf("addressListeningUDP6: %v", addressListeningUDP6)
|
||||
t.Logf("globalListeningUDP6: %v", globalListeningUDP6)
|
||||
|
||||
getListeningSocket(&net.IPv4zero, 53, TCP4)
|
||||
getListeningSocket(&net.IPv4zero, 53, UDP4)
|
||||
getListeningSocket(&net.IPv6zero, 53, TCP6)
|
||||
getListeningSocket(&net.IPv6zero, 53, UDP6)
|
||||
getListeningSocket(net.IPv4zero, 53, TCP4)
|
||||
getListeningSocket(net.IPv4zero, 53, UDP4)
|
||||
getListeningSocket(net.IPv6zero, 53, TCP6)
|
||||
getListeningSocket(net.IPv6zero, 53, UDP6)
|
||||
|
||||
// spotify: 192.168.0.102:5353 192.121.140.65:80
|
||||
localIP := net.IPv4(192, 168, 127, 10)
|
||||
uid, inode, ok := getConnectionSocket(&localIP, 46634, TCP4)
|
||||
uid, inode, ok := getConnectionSocket(localIP, 46634, TCP4)
|
||||
t.Logf("getConnectionSocket: %d %d %v", uid, inode, ok)
|
||||
|
||||
activeConnectionIDs := GetActiveConnectionIDs()
|
||||
|
||||
@@ -49,11 +49,17 @@ type Process struct {
|
||||
|
||||
// ProfileSet returns the assigned profile set.
|
||||
func (p *Process) ProfileSet() *profile.Set {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
return p.profileSet
|
||||
}
|
||||
|
||||
// Strings returns a string represenation of process.
|
||||
func (p *Process) String() string {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
if p == nil {
|
||||
return "?"
|
||||
}
|
||||
@@ -64,6 +70,7 @@ func (p *Process) String() string {
|
||||
func (p *Process) AddConnection() {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
p.ConnectionCount++
|
||||
p.LastConnectionEstablished = time.Now().Unix()
|
||||
if p.FirstConnectionEstablished == 0 {
|
||||
@@ -75,6 +82,7 @@ func (p *Process) AddConnection() {
|
||||
func (p *Process) RemoveConnection() {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
if p.ConnectionCount > 0 {
|
||||
p.ConnectionCount--
|
||||
}
|
||||
@@ -235,9 +243,8 @@ func GetOrFindProcess(pid int) (*Process, error) {
|
||||
// Executable Information
|
||||
|
||||
// FIXME: use os specific path seperator
|
||||
splittedPath := strings.Split("/", new.Path)
|
||||
new.ExecName = strings.ToTitle(splittedPath[len(splittedPath)-1])
|
||||
|
||||
splittedPath := strings.Split(new.Path, "/")
|
||||
new.ExecName = splittedPath[len(splittedPath)-1]
|
||||
}
|
||||
|
||||
// save to storage
|
||||
|
||||
Reference in New Issue
Block a user