mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-03-25 23:30:41 +01:00
feat: robust username detection and CI updates
Add platform-specific username detection for Control D metadata: - macOS: directory services (dscl) with console user fallback - Linux: systemd loginctl, utmp, /etc/passwd traversal - Windows: WTS session enumeration, registry, token lookup
This commit is contained in:
committed by
Cuong Manh Le
parent
e7040bd9f9
commit
9b2e51f53a
66
metadata.go
66
metadata.go
@@ -2,11 +2,6 @@ package ctrld
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/cuonglm/osinfo"
|
||||
|
||||
@@ -27,8 +22,21 @@ var (
|
||||
chassisVendor string
|
||||
)
|
||||
|
||||
// SystemMetadata collects system and user-related SystemMetadata and returns it as a map.
|
||||
// SystemMetadata collects full system metadata including username discovery.
|
||||
// Use for initial provisioning and first-run config validation where full
|
||||
// device identification is needed.
|
||||
func SystemMetadata(ctx context.Context) map[string]string {
|
||||
return systemMetadata(ctx, true)
|
||||
}
|
||||
|
||||
// SystemMetadataRuntime collects system metadata without username discovery.
|
||||
// Use for runtime API calls (config reload, self-uninstall check, deactivation
|
||||
// pin refresh) to avoid repeated user enumeration that can trigger EDR alerts.
|
||||
func SystemMetadataRuntime(ctx context.Context) map[string]string {
|
||||
return systemMetadata(ctx, false)
|
||||
}
|
||||
|
||||
func systemMetadata(ctx context.Context, includeUsername bool) map[string]string {
|
||||
m := make(map[string]string)
|
||||
oi := osinfo.New()
|
||||
m[metadataOsKey] = oi.String()
|
||||
@@ -39,7 +47,9 @@ func SystemMetadata(ctx context.Context) map[string]string {
|
||||
}
|
||||
m[metadataChassisTypeKey] = chassisType
|
||||
m[metadataChassisVendorKey] = chassisVendor
|
||||
m[metadataUsernameKey] = currentLoginUser(ctx)
|
||||
if includeUsername {
|
||||
m[metadataUsernameKey] = DiscoverMainUser(ctx)
|
||||
}
|
||||
m[metadataDomainOrWorkgroupKey] = partOfDomainOrWorkgroup(ctx)
|
||||
domain, err := system.GetActiveDirectoryDomain()
|
||||
if err != nil {
|
||||
@@ -49,45 +59,3 @@ func SystemMetadata(ctx context.Context) map[string]string {
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// currentLoginUser attempts to find the actual login user, even if the process is running as root.
|
||||
func currentLoginUser(ctx context.Context) string {
|
||||
// On Darwin 26.2+, sudo no longer preserves SUDO_USER, LOGNAME, USER etc., so we cannot
|
||||
// rely on environment variables when running under sudo. See CVE-2025-43416.
|
||||
// We use the logname(1) command on Unix, which reports the login name from the session
|
||||
// (e.g. utmp); there is no portable syscall equivalent in Go, so we exec logname.
|
||||
if runtime.GOOS != "windows" {
|
||||
if name := runLogname(ctx); name != "" {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: env vars (still set on older systems or when not using sudo)
|
||||
if u := os.Getenv("SUDO_USER"); u != "" {
|
||||
return u
|
||||
}
|
||||
if u := os.Getenv("LOGNAME"); u != "" {
|
||||
return u
|
||||
}
|
||||
if u := os.Getenv("USER"); u != "" {
|
||||
return u
|
||||
}
|
||||
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
ProxyLogger.Load().Debug().Err(err).Msg("Failed to get current user")
|
||||
return "unknown"
|
||||
}
|
||||
return currentUser.Username
|
||||
}
|
||||
|
||||
// runLogname runs the logname(1) command and returns the trimmed output, or "" on failure.
|
||||
func runLogname(ctx context.Context) string {
|
||||
cmd := exec.CommandContext(ctx, "logname")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
ProxyLogger.Load().Debug().Err(err).Msg("Failed to run logname")
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user