diff --git a/network/connection.go b/network/connection.go index 9ac66e23..f17b4e2e 100644 --- a/network/connection.go +++ b/network/connection.go @@ -165,15 +165,26 @@ type Reason struct { Context interface{} } -func getProcessContext(proc *process.Process) ProcessContext { - return ProcessContext{ +func getProcessContext(ctx context.Context, proc *process.Process) ProcessContext { + // Gather process information. + pCtx := ProcessContext{ BinaryPath: proc.Path, ProcessName: proc.Name, - ProfileName: proc.Profile().LocalProfile().Name, PID: proc.Pid, - Profile: proc.Profile().LocalProfile().ID, - Source: string(proc.Profile().LocalProfile().Source), } + + // Get local profile. + localProfile := proc.Profile().LocalProfile() + if localProfile == nil { + log.Tracer(ctx).Warningf("network: process %s has no profile", proc) + return pCtx + } + + // Add profile information and return. + pCtx.ProfileName = localProfile.Name + pCtx.Profile = localProfile.ID + pCtx.Source = string(localProfile.Source) + return pCtx } // NewConnectionFromDNSRequest returns a new connection based on the given dns request. @@ -204,7 +215,7 @@ func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []stri CNAME: cnames, }, process: proc, - ProcessContext: getProcessContext(proc), + ProcessContext: getProcessContext(ctx, proc), Started: timestamp, Ended: timestamp, } @@ -304,7 +315,7 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection { IPProtocol: pkt.Info().Protocol, LocalIP: pkt.Info().LocalIP(), LocalPort: pkt.Info().LocalPort(), - ProcessContext: getProcessContext(proc), + ProcessContext: getProcessContext(pkt.Ctx(), proc), process: proc, // remote endpoint Entity: entity, diff --git a/process/process.go b/process/process.go index 9d932555..e99d0b3b 100644 --- a/process/process.go +++ b/process/process.go @@ -232,89 +232,83 @@ func loadProcess(ctx context.Context, pid int) (*Process, error) { defer markRequestFinished() } - // create new process + // Create new a process object. new := &Process{ Pid: pid, Virtual: true, // caller must decide to actually use the process - we need to save now. FirstSeen: time.Now().Unix(), } - switch { - case new.IsKernel(): - new.UserName = "Kernel" - new.Name = "Operating System" - default: - - pInfo, err := processInfo.NewProcess(int32(pid)) - if err != nil { - return nil, err - } - - // UID - // net yet implemented for windows - if runtime.GOOS == "linux" { - var uids []int32 - uids, err = pInfo.Uids() - if err != nil { - return nil, fmt.Errorf("failed to get UID for p%d: %s", pid, err) - } - new.UserID = int(uids[0]) - } - - // Username - new.UserName, err = pInfo.Username() - if err != nil { - return nil, fmt.Errorf("process: failed to get Username for p%d: %s", pid, err) - } - - // TODO: User Home - // new.UserHome, err = - - // PPID - ppid, err := pInfo.Ppid() - if err != nil { - return nil, fmt.Errorf("failed to get PPID for p%d: %s", pid, err) - } - new.ParentPid = int(ppid) - - // Path - new.Path, err = pInfo.Exe() - if err != nil { - return nil, fmt.Errorf("failed to get Path for p%d: %s", pid, err) - } - // remove linux " (deleted)" suffix for deleted files - if onLinux { - new.Path = strings.TrimSuffix(new.Path, " (deleted)") - } - // Executable Name - _, new.ExecName = filepath.Split(new.Path) - - // Current working directory - // net yet implemented for windows - // new.Cwd, err = pInfo.Cwd() - // if err != nil { - // log.Warningf("process: failed to get Cwd: %s", err) - // } - - // Command line arguments - new.CmdLine, err = pInfo.Cmdline() - if err != nil { - return nil, fmt.Errorf("failed to get Cmdline for p%d: %s", pid, err) - } - - // Name - new.Name, err = pInfo.Name() - if err != nil { - return nil, fmt.Errorf("failed to get Name for p%d: %s", pid, err) - } - if new.Name == "" { - new.Name = new.ExecName - } - - // OS specifics - new.specialOSInit() + // Get process information from the system. + pInfo, err := processInfo.NewProcess(int32(pid)) + if err != nil { + return nil, err } + // UID + // net yet implemented for windows + if runtime.GOOS == "linux" { + var uids []int32 + uids, err = pInfo.Uids() + if err != nil { + return nil, fmt.Errorf("failed to get UID for p%d: %s", pid, err) + } + new.UserID = int(uids[0]) + } + + // Username + new.UserName, err = pInfo.Username() + if err != nil { + return nil, fmt.Errorf("process: failed to get Username for p%d: %s", pid, err) + } + + // TODO: User Home + // new.UserHome, err = + + // PPID + ppid, err := pInfo.Ppid() + if err != nil { + return nil, fmt.Errorf("failed to get PPID for p%d: %s", pid, err) + } + new.ParentPid = int(ppid) + + // Path + new.Path, err = pInfo.Exe() + if err != nil { + return nil, fmt.Errorf("failed to get Path for p%d: %s", pid, err) + } + // remove linux " (deleted)" suffix for deleted files + if onLinux { + new.Path = strings.TrimSuffix(new.Path, " (deleted)") + } + // Executable Name + _, new.ExecName = filepath.Split(new.Path) + + // Current working directory + // net yet implemented for windows + // new.Cwd, err = pInfo.Cwd() + // if err != nil { + // log.Warningf("process: failed to get Cwd: %s", err) + // } + + // Command line arguments + new.CmdLine, err = pInfo.Cmdline() + if err != nil { + return nil, fmt.Errorf("failed to get Cmdline for p%d: %s", pid, err) + } + + // Name + new.Name, err = pInfo.Name() + if err != nil { + return nil, fmt.Errorf("failed to get Name for p%d: %s", pid, err) + } + if new.Name == "" { + new.Name = new.ExecName + } + + // OS specifics + new.specialOSInit() + new.Save() return new, nil } diff --git a/process/process_default.go b/process/process_default.go index 5266462b..97f093e9 100644 --- a/process/process_default.go +++ b/process/process_default.go @@ -2,10 +2,8 @@ package process -// IsKernel returns whether the process is the Kernel. -func (p *Process) IsKernel() bool { - return p.Pid == 0 -} +// SystemProcessID is the PID of the System/Kernel itself. +const SystemProcessID = 0 // specialOSInit does special OS specific Process initialization. func (p *Process) specialOSInit() { diff --git a/process/process_linux.go b/process/process_linux.go index 90dcfecc..bd6a1fb6 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -1,9 +1,7 @@ package process -// IsKernel returns whether the process is the Kernel. -func (p *Process) IsKernel() bool { - return p.Pid == 0 -} +// SystemProcessID is the PID of the System/Kernel itself. +const SystemProcessID = 0 // specialOSInit does special OS specific Process initialization. func (p *Process) specialOSInit() { diff --git a/process/process_windows.go b/process/process_windows.go index 2a59c3d1..c202bcb9 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -7,10 +7,8 @@ import ( "github.com/safing/portbase/utils/osdetail" ) -// IsKernel returns whether the process is the Kernel. -func (p *Process) IsKernel() bool { - return p.Pid == 4 -} +// SystemProcessID is the PID of the System/Kernel itself. +const SystemProcessID = 4 // specialOSInit does special OS specific Process initialization. func (p *Process) specialOSInit() { diff --git a/process/special.go b/process/special.go index 2676f271..829e16aa 100644 --- a/process/special.go +++ b/process/special.go @@ -9,11 +9,9 @@ import ( "golang.org/x/sync/singleflight" ) -// Special Process IDs -const ( - UnidentifiedProcessID = -1 - SystemProcessID = 0 -) +// UnidentifiedProcessID is the PID used for anything that could not be +// attributed to a PID for any reason. +const UnidentifiedProcessID = -1 var ( // unidentifiedProcess is used when a process cannot be found. @@ -39,18 +37,18 @@ var ( // GetUnidentifiedProcess returns the special process assigned to unidentified processes. func GetUnidentifiedProcess(ctx context.Context) *Process { - return getSpecialProcess(ctx, UnidentifiedProcessID, unidentifiedProcess) + return getSpecialProcess(ctx, unidentifiedProcess) } // GetSystemProcess returns the special process used for the Kernel. func GetSystemProcess(ctx context.Context) *Process { - return getSpecialProcess(ctx, SystemProcessID, systemProcess) + return getSpecialProcess(ctx, systemProcess) } -func getSpecialProcess(ctx context.Context, pid int, template *Process) *Process { - p, _, _ := getSpecialProcessSingleInflight.Do(strconv.Itoa(pid), func() (interface{}, error) { +func getSpecialProcess(ctx context.Context, template *Process) *Process { + p, _, _ := getSpecialProcessSingleInflight.Do(strconv.Itoa(template.Pid), func() (interface{}, error) { // Check if we have already loaded the special process. - process, ok := GetProcessFromStorage(pid) + process, ok := GetProcessFromStorage(template.Pid) if ok { return process, nil } diff --git a/profile/profile-layered.go b/profile/profile-layered.go index 4f5d29f0..897ffb22 100644 --- a/profile/profile-layered.go +++ b/profile/profile-layered.go @@ -154,6 +154,10 @@ func (lp *LayeredProfile) UnlockForUsage() { // LocalProfile returns the local profile associated with this layered profile. func (lp *LayeredProfile) LocalProfile() *Profile { + if lp == nil { + return nil + } + lp.RLock() defer lp.RUnlock() diff --git a/profile/profile.go b/profile/profile.go index b1798b07..0613c187 100644 --- a/profile/profile.go +++ b/profile/profile.go @@ -386,22 +386,22 @@ func EnsureProfile(r record.Record) (*Profile, error) { // the profile was changed. If there is data that needs to be fetched from the // operating system, it will start an async worker to fetch that data and save // the profile afterwards. -func (p *Profile) UpdateMetadata(processName string) (changed bool) { +func (profile *Profile) UpdateMetadata(processName string) (changed bool) { // Check if this is a local profile, else warn and return. - if p.Source != SourceLocal { - log.Warningf("tried to update metadata for non-local profile %s", p.ScopedID()) + if profile.Source != SourceLocal { + log.Warningf("tried to update metadata for non-local profile %s", profile.ScopedID()) return false } - p.Lock() - defer p.Unlock() + profile.Lock() + defer profile.Unlock() // Check if this is a special profile. - if p.LinkedPath == "" { + if profile.LinkedPath == "" { // This is a special profile, just assign the processName, if needed, and // return. - if p.Name != processName { - p.Name = processName + if profile.Name != processName { + profile.Name = processName return true } return false @@ -410,18 +410,18 @@ func (p *Profile) UpdateMetadata(processName string) (changed bool) { var needsUpdateFromSystem bool // Check profile name. - _, filename := filepath.Split(p.LinkedPath) + _, filename := filepath.Split(profile.LinkedPath) // Update profile name if it is empty or equals the filename, which is the // case for older profiles. - if p.Name == "" || p.Name == filename { + if profile.Name == "" || profile.Name == filename { // Generate a default profile name if does not exist. - p.Name = osdetail.GenerateBinaryNameFromPath(p.LinkedPath) - if p.Name == filename { + profile.Name = osdetail.GenerateBinaryNameFromPath(profile.LinkedPath) + if profile.Name == filename { // TODO: Theoretically, the generated name could be identical to the // filename. // As a quick fix, append a space to the name. - p.Name += " " + profile.Name += " " } changed = true needsUpdateFromSystem = true @@ -429,7 +429,7 @@ func (p *Profile) UpdateMetadata(processName string) (changed bool) { // If needed, get more/better data from the operating system. if needsUpdateFromSystem { - module.StartWorker("get profile metadata", p.updateMetadataFromSystem) + module.StartWorker("get profile metadata", profile.updateMetadataFromSystem) } return changed @@ -437,32 +437,34 @@ func (p *Profile) UpdateMetadata(processName string) (changed bool) { // updateMetadataFromSystem updates the profile metadata with data from the // operating system and saves it afterwards. -func (p *Profile) updateMetadataFromSystem(ctx context.Context) error { +func (profile *Profile) updateMetadataFromSystem(ctx context.Context) error { // This function is only valid for local profiles. - if p.Source != SourceLocal || p.LinkedPath == "" { - return fmt.Errorf("tried to update metadata for non-local / non-linked profile %s", p.ScopedID()) + if profile.Source != SourceLocal || profile.LinkedPath == "" { + return fmt.Errorf("tried to update metadata for non-local / non-linked profile %s", profile.ScopedID()) } // Save the profile when finished, if needed. save := false defer func() { if save { - err := p.Save() + err := profile.Save() if err != nil { - log.Warningf("profile: failed to save %s after metadata update: %s", p.ScopedID(), err) + log.Warningf("profile: failed to save %s after metadata update: %s", profile.ScopedID(), err) } } }() // Get binary name from linked path. - newName, err := osdetail.GetBinaryNameFromSystem(p.LinkedPath) + newName, err := osdetail.GetBinaryNameFromSystem(profile.LinkedPath) if err != nil { - log.Warningf("profile: error while getting binary name for %s: %s", p.LinkedPath, err) + if !errors.Is(err, osdetail.ErrNotSupported) { + log.Warningf("profile: error while getting binary name for %s: %s", profile.LinkedPath, err) + } return nil } // Get filename of linked path for comparison. - _, filename := filepath.Split(p.LinkedPath) + _, filename := filepath.Split(profile.LinkedPath) // TODO: Theoretically, the generated name from the system could be identical // to the filename. This would mean that the worker is triggered every time @@ -473,12 +475,12 @@ func (p *Profile) updateMetadataFromSystem(ctx context.Context) error { } // Lock profile for applying metadata. - p.Lock() - defer p.Unlock() + profile.Lock() + defer profile.Unlock() // Apply new name if it changed. - if p.Name != newName { - p.Name = newName + if profile.Name != newName { + profile.Name = newName save = true }