package security import ( "fmt" "os" "os/exec" "syscall" "git.gostacks.org/iwasforcedtobehere/WiseTLP/autotlp/pkg/utils" ) // PrivilegeManager handles privilege escalation and security operations type PrivilegeManager struct { logger *utils.Logger } // NewPrivilegeManager creates a new privilege manager func NewPrivilegeManager(logger *utils.Logger) *PrivilegeManager { return &PrivilegeManager{ logger: logger.WithComponent("security"), } } // RequireRoot ensures the current operation has root privileges func (pm *PrivilegeManager) RequireRoot(operation string) error { if os.Geteuid() == 0 { return nil } pm.logger.Info("Root privileges required", "operation", operation) return fmt.Errorf("operation '%s' requires root privileges", operation) } // EscalateIfNeeded escalates privileges if not already running as root func (pm *PrivilegeManager) EscalateIfNeeded(args []string, operation string) error { if os.Geteuid() == 0 { return nil } pm.logger.Info("Escalating privileges", "operation", operation) // Check if sudo is available if !utils.CommandExists("sudo") { return fmt.Errorf("sudo is required for privilege escalation but not available") } // Inform user about privilege escalation fmt.Printf("\nPrivilege escalation required for: %s\n", operation) fmt.Println("This operation needs administrative privileges to:") switch operation { case "install_tlp": fmt.Println("- Install TLP packages using system package manager") fmt.Println("- Enable and start TLP systemd service") fmt.Println("- Mask conflicting power management services") case "apply_config": fmt.Println("- Write TLP configuration to system directories") fmt.Println("- Reload TLP service with new configuration") case "system_info": fmt.Println("- Access hardware information from system files") fmt.Println("- Read power management settings") default: fmt.Printf("- Perform system operation: %s\n", operation) } if !utils.GetUserConfirmation("Continue with privilege escalation?") { return fmt.Errorf("privilege escalation declined by user") } // Execute with sudo return pm.executeSudo(args) } // executeSudo executes the current program with sudo func (pm *PrivilegeManager) executeSudo(args []string) error { // Get current executable path executable, err := os.Executable() if err != nil { return fmt.Errorf("failed to get executable path: %w", err) } // Prepare sudo command sudoArgs := append([]string{executable}, args...) cmd := exec.Command("sudo", sudoArgs...) // Preserve environment variables that might be needed cmd.Env = os.Environ() // Connect stdio cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr pm.logger.Info("Executing with sudo", "command", cmd.String()) // Execute the command err = cmd.Run() if err != nil { if exitError, ok := err.(*exec.ExitError); ok { // Get exit code from the elevated process if status, ok := exitError.Sys().(syscall.WaitStatus); ok { os.Exit(status.ExitStatus()) } } return fmt.Errorf("sudo execution failed: %w", err) } // If we reach here, the elevated process completed successfully os.Exit(0) return nil // This line will never be reached } // CheckSudoAccess verifies that the user can use sudo func (pm *PrivilegeManager) CheckSudoAccess() error { if os.Geteuid() == 0 { return nil // Already root } if !utils.CommandExists("sudo") { return fmt.Errorf("sudo command not available") } // Test sudo access with a harmless command cmd := exec.Command("sudo", "-n", "true") if err := cmd.Run(); err != nil { // -n flag failed, user needs to authenticate pm.logger.Debug("Sudo authentication required") return nil // This is expected for most users } pm.logger.Debug("Sudo access confirmed without authentication") return nil } // DropPrivileges drops root privileges if running as root func (pm *PrivilegeManager) DropPrivileges() error { if os.Geteuid() != 0 { return nil // Not running as root } // Get the original user info from environment sudoUID := os.Getenv("SUDO_UID") sudoGID := os.Getenv("SUDO_GID") if sudoUID == "" || sudoGID == "" { pm.logger.Warn("Cannot drop privileges: SUDO_UID/SUDO_GID not set") return nil // Don't fail, just log warning } pm.logger.Info("Dropping root privileges", "uid", sudoUID, "gid", sudoGID) // This is a placeholder - actual privilege dropping requires careful handling // and is typically done at the start of the program, not mid-execution pm.logger.Debug("Privilege dropping not implemented for mid-execution") return nil } // ValidateSystemAccess validates that the program has necessary system access func (pm *PrivilegeManager) ValidateSystemAccess() error { // Check read access to system information systemPaths := []string{ "/proc/cpuinfo", "/proc/meminfo", "/sys/class/power_supply", "/sys/devices/system/cpu", } for _, path := range systemPaths { if utils.FileExists(path) { // Try to read the file/directory if file, err := os.Open(path); err != nil { pm.logger.Warn("Limited access to system path", "path", path, "error", err) } else { file.Close() } } } return nil } // SecureFilePermissions sets secure permissions on configuration files func (pm *PrivilegeManager) SecureFilePermissions(filePath string) error { // Set file permissions to be readable only by owner and group if err := os.Chmod(filePath, 0640); err != nil { return fmt.Errorf("failed to set secure permissions on %s: %w", filePath, err) } pm.logger.Debug("Set secure file permissions", "file", filePath, "mode", "0640") return nil } // ValidateInput performs basic input validation for security func ValidateInput(input string, maxLength int, allowedChars string) error { if len(input) > maxLength { return fmt.Errorf("input too long: %d characters (max %d)", len(input), maxLength) } if len(input) == 0 { return fmt.Errorf("input cannot be empty") } // Basic validation against null bytes and control characters for i, r := range input { if r == 0 { return fmt.Errorf("null byte at position %d", i) } if r < 32 && r != 9 && r != 10 && r != 13 { // Allow tab, LF, CR return fmt.Errorf("control character at position %d", i) } } return nil } // SanitizeFilePath sanitizes file paths to prevent directory traversal func SanitizeFilePath(path string) (string, error) { // Basic path validation if path == "" { return "", fmt.Errorf("empty path") } // Check for directory traversal attempts if len(path) > 1 && (path[0] == '/' || path[1] == ':') { // Absolute path - this might be intentional, so we allow it // but log it for security awareness } // Check for dangerous patterns dangerousPatterns := []string{ "../", "..\\", "./", ".\\", } for _, pattern := range dangerousPatterns { if len(path) >= len(pattern) { for i := 0; i <= len(path)-len(pattern); i++ { if path[i:i+len(pattern)] == pattern { return "", fmt.Errorf("potentially dangerous path pattern: %s", pattern) } } } } return path, nil }