You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
98 lines
2.4 KiB
98 lines
2.4 KiB
package scanner
|
|
|
|
// Copyright (c) 2025 Robert Strutts <bobs@NewToFaith.com>
|
|
// License: MIT
|
|
// GIT: https://git.mysnippetsofcode.com/bobs/execguard
|
|
|
|
import (
|
|
"execguard/core/alert"
|
|
"execguard/core/configure"
|
|
"execguard/core/sys_database"
|
|
"database/sql"
|
|
"path/filepath"
|
|
"time"
|
|
"strings"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"io/fs"
|
|
"sync"
|
|
)
|
|
|
|
|
|
var (
|
|
config configure.Config
|
|
initMode bool
|
|
initFile string
|
|
updateFile string
|
|
migrateMode bool
|
|
dbMutex sync.Mutex
|
|
alertCache sync.Map
|
|
)
|
|
|
|
func SetModes(mode bool, file string, update string, migrate bool) {
|
|
initMode = mode
|
|
initFile = file
|
|
updateFile = update
|
|
migrateMode = migrate
|
|
}
|
|
|
|
func SetGlobalConfig(c configure.Config) {
|
|
config = c
|
|
}
|
|
|
|
func PeriodicScan(dirs []string, db *sql.DB, log log.Logger, mailPath string, scanInterval int) {
|
|
skipSet := make(map[string]struct{})
|
|
for _, skip := range config.SkipDirs {
|
|
if abs, err := filepath.Abs(skip); err == nil {
|
|
skipSet[abs] = struct{}{}
|
|
}
|
|
}
|
|
interval := time.Duration(scanInterval) * time.Minute
|
|
// log.Printf("Starting periodic scan every %v...", interval)
|
|
for {
|
|
for _, dir := range dirs {
|
|
filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
absPath, err := filepath.Abs(path)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
// Skip if in any of the SkipDirs
|
|
for skipDir := range skipSet {
|
|
if strings.HasPrefix(absPath, skipDir) {
|
|
return filepath.SkipDir
|
|
}
|
|
}
|
|
|
|
if d.Type().IsRegular() {
|
|
info, err := d.Info()
|
|
if err != nil || (info.Mode().Perm()&0111 == 0) {
|
|
return nil
|
|
}
|
|
absPath, _ = filepath.EvalSymlinks(absPath)
|
|
|
|
if initMode {
|
|
sys_database.AddToAllowed(db, log, absPath)
|
|
} else if !sys_database.IsAllowed(db, log, absPath) {
|
|
log.Printf("Found unauthorized executable: %s", absPath)
|
|
os.Chmod(absPath, info.Mode()&^0111)
|
|
|
|
if _, seen := alertCache.LoadOrStore(absPath, struct{}{}); !seen {
|
|
go alert.SendAlert(fmt.Sprintf("Unauthorized execution attempt blocked: %s", absPath), db, log)
|
|
time.AfterFunc(10*time.Minute, func() {
|
|
alertCache.Delete(absPath)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
time.Sleep(interval)
|
|
}
|
|
}
|
|
|