safing-portmaster/service/process/find.go

163 lines
4.9 KiB
Go

package process
import (
"context"
"fmt"
"net"
"time"
"github.com/safing/portbase/api"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/service/network/netutils"
"github.com/safing/portmaster/service/network/packet"
"github.com/safing/portmaster/service/network/reference"
"github.com/safing/portmaster/service/network/state"
"github.com/safing/portmaster/service/profile"
)
// GetProcessWithProfile returns the process, including the profile.
// Always returns valid data.
// Errors are logged and returned for information or special handling purposes.
func GetProcessWithProfile(ctx context.Context, pid int) (process *Process, err error) {
if !enableProcessDetection() {
log.Tracer(ctx).Tracef("process: process detection disabled")
return GetUnidentifiedProcess(ctx), nil
}
process, err = GetOrFindProcess(ctx, pid)
if err != nil {
log.Tracer(ctx).Debugf("process: failed to find process with PID: %s", err)
return GetUnidentifiedProcess(ctx), err
}
// Get process group leader, which is the process "nearest" to the user and
// will have more/better information for finding names ans icons, for example.
err = process.FindProcessGroupLeader(ctx)
if err != nil {
log.Warningf("process: failed to get process group leader for %s: %s", process, err)
}
changed, err := process.GetProfile(ctx)
if err != nil {
log.Tracer(ctx).Errorf("process: failed to get profile for process %s: %s", process, err)
}
if changed {
process.Save()
}
return process, nil
}
// GetPidOfConnection returns the PID of the process that owns the described connection.
// Always returns valid data.
// Errors are logged and returned for information or special handling purposes.
func GetPidOfConnection(ctx context.Context, pktInfo *packet.Info) (pid int, connInbound bool, err error) {
if !enableProcessDetection() {
return UnidentifiedProcessID, pktInfo.Inbound, nil
}
// Use fast search for inbound packets, as the listening socket should
// already be there for a while now.
fastSearch := pktInfo.Inbound
connInbound = pktInfo.Inbound
// Check if we need to get the PID.
if pktInfo.PID == UndefinedProcessID {
log.Tracer(ctx).Tracef("process: getting pid from system network state")
pid, connInbound, err = state.Lookup(pktInfo, fastSearch)
if err != nil {
err = fmt.Errorf("failed to find PID of connection: %w", err)
log.Tracer(ctx).Tracef("process: %s", err)
pid = UndefinedProcessID
}
} else {
log.Tracer(ctx).Tracef("process: pid already set in packet (by ebpf or kext)")
pid = pktInfo.PID
}
// Fallback to special profiles if PID could not be found.
if pid == UndefinedProcessID {
switch {
case !connInbound:
pid = UnidentifiedProcessID
case netutils.ClassifyIP(pktInfo.Dst).IsLocalhost():
// Always treat localhost connections as unidentified/unknown.
pid = UnidentifiedProcessID
case reference.IsICMP(uint8(pktInfo.Protocol)):
// Always treat ICMP as unidentified/unknown, as the direction
// might change to outgoing by new ICMP echo packets.
pid = UnidentifiedProcessID
default:
// All other inbound connections are "unsolicited".
pid = UnsolicitedProcessID
}
}
return pid, connInbound, err
}
// GetNetworkHost returns a *Process that represents a host on the network.
func GetNetworkHost(ctx context.Context, remoteIP net.IP) (process *Process, err error) { //nolint:interfacer
now := time.Now().Unix()
networkHost := &Process{
Name: fmt.Sprintf("Device at %s", remoteIP),
UserName: "N/A",
UserID: NetworkHostProcessID,
Pid: NetworkHostProcessID,
ParentPid: NetworkHostProcessID,
Tags: []profile.Tag{
{
Key: "ip",
Value: remoteIP.String(),
},
},
FirstSeen: now,
LastSeen: now,
}
// Get the (linked) local profile.
networkHostProfile, err := profile.GetLocalProfile("", networkHost.MatchingData(), networkHost.CreateProfileCallback)
if err != nil {
return nil, err
}
// Assign profile to process.
networkHost.PrimaryProfileID = networkHostProfile.ScopedID()
networkHost.profile = networkHostProfile.LayeredProfile()
return networkHost, nil
}
// GetProcessByRequestOrigin returns the process that initiated the API request ar.
func GetProcessByRequestOrigin(ar *api.Request) (*Process, error) {
// get remote IP/Port
remoteIP, remotePort, err := netutils.ParseIPPort(ar.RemoteAddr)
if err != nil {
return nil, fmt.Errorf("failed to get remote IP/Port: %w", err)
}
pkt := &packet.Info{
Inbound: false, // outbound as we are looking for the process of the source address
Version: packet.IPv4,
Protocol: packet.TCP,
Src: remoteIP, // source as in the process we are looking for
SrcPort: remotePort, // source as in the process we are looking for
PID: UndefinedProcessID,
}
pid, _, err := GetPidOfConnection(ar.Context(), pkt)
if err != nil {
return nil, err
}
proc, err := GetProcessWithProfile(ar.Context(), pid)
if err != nil {
return nil, err
}
return proc, nil
}