Skip to content

Commit

Permalink
POC aeaconf integration with obfuscation and sample functions
Browse files Browse the repository at this point in the history
  • Loading branch information
safinsingh committed Oct 30, 2023
1 parent 5e1356c commit 990bc25
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 133 deletions.
89 changes: 44 additions & 45 deletions checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,21 @@ import (
"reflect"
"regexp"
"strings"

"github.com/safinsingh/aeaconf2"
)

// check is the smallest unit that can show up on a scoring report. It holds all
// the conditions for a check, and its message and points (autogenerated or
// otherwise).
type check struct {
Message string
Hint string
Points int

Fail []cond
Pass []cond
PassOverride []cond
}
var funcRegistry map[string]reflect.Type

// cond, or condition, is the parameters for a given test within a check.
type cond struct {
Hint string
Type string
func init() {
funcRegistry = make(map[string]reflect.Type)

Path string
Cmd string
User string
Group string
Name string
Key string
Value string
After string
regex bool
funcRegistry["AutoCheckUpdatesEnabled"] = reflect.TypeOf(AutoCheckUpdatesEnabled{})
funcRegistry["DirContains"] = reflect.TypeOf(DirContains{})
funcRegistry["FileContains"] = reflect.TypeOf(FileContains{})
funcRegistry["PathExists"] = reflect.TypeOf(PathExists{})

aeaconf2.CheckFunctionRegistry(funcRegistry)
}

// requireArgs is a convenience function that prints a warning if any required
Expand Down Expand Up @@ -104,11 +90,11 @@ func handleReflectPanic(condFunc string) {
}

// runCheck executes a single condition check.
func runCheck(cond cond) bool {
if err := deobfuscateCond(&cond); err != nil {
func runCheck(cond aeaconf2.Condition) bool {
if err := deobfuscateCond(cond); err != nil {
fail(err.Error())
}
defer obfuscateCond(&cond)
defer obfuscateCond(cond)
debug("Running condition:\n", cond)

not := "Not"
Expand Down Expand Up @@ -178,11 +164,15 @@ func (c cond) CommandOutput() (bool, error) {
}

// DirContains returns true if any file in the directory contains the string value provided.
func (c cond) DirContains() (bool, error) {
c.requireArgs("Path", "Value")
result, err := cond{
Path: c.Path,
}.PathExists()
type DirContains struct {
aeaconf2.BaseCondition
Path string
Value string
Regex bool
}

func (d *DirContains) Score() (bool, error) {
result, err := (&PathExists{Path: d.Path}).Score()
if err != nil {
return false, err
}
Expand All @@ -191,7 +181,7 @@ func (c cond) DirContains() (bool, error) {
}

var files []string
err = filepath.Walk(c.Path, func(path string, info os.FileInfo, err error) error {
err = filepath.Walk(d.Path, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
files = append(files, path)
}
Expand All @@ -206,8 +196,7 @@ func (c cond) DirContains() (bool, error) {
}

for _, file := range files {
c.Path = file
result, err := c.FileContains()
result, err := (&FileContains{Path: file, Value: d.Value}).Score()
if os.IsPermission(err) {
return false, err
}
Expand All @@ -222,21 +211,27 @@ func (c cond) DirContains() (bool, error) {
//
// Newlines in regex may not work as expected, especially on Windows. It's
// best to not use these (ex. ^ and $).
func (c cond) FileContains() (bool, error) {
c.requireArgs("Path", "Value")
fileContent, err := readFile(c.Path)
type FileContains struct {
aeaconf2.BaseCondition
Path string
Value string
Regex bool
}

func (f *FileContains) Score() (bool, error) {
fileContent, err := readFile(f.Path)
if err != nil {
return false, err
}
found := false
for _, line := range strings.Split(fileContent, "\n") {
if c.regex {
found, err = regexp.Match(c.Value, []byte(line))
if f.Regex {
found, err = regexp.Match(f.Value, []byte(line))
if err != nil {
return false, err
}
} else {
found = strings.Contains(line, c.Value)
found = strings.Contains(line, f.Value)
}
if found {
break
Expand Down Expand Up @@ -264,9 +259,13 @@ func (c cond) FileEquals() (bool, error) {

// PathExists is a wrapper around os.Stat and os.IsNotExist, and determines
// whether a file or folder exists.
func (c cond) PathExists() (bool, error) {
c.requireArgs("Path")
_, err := os.Stat(c.Path)
type PathExists struct {
aeaconf2.BaseCondition
Path string
}

func (p *PathExists) Score() (bool, error) {
_, err := os.Stat(p.Path)
if err != nil && os.IsNotExist(err) {
return false, nil
} else if err != nil {
Expand Down
8 changes: 5 additions & 3 deletions checks_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import (
"syscall"
)

func (c cond) AutoCheckUpdatesEnabled() (bool, error) {
result, err := cond{
type AutoCheckUpdatesEnabled struct{}

func (a *AutoCheckUpdatesEnabled) Score() (bool, error) {
result, err := DirContains{
Path: "/etc/apt/apt.conf.d/",
Value: `(?i)^\s*APT::Periodic::Update-Package-Lists\s+"1"\s*;\s*$`,
regex: true,
}.DirContains()
}.Score()
// If /etc/apt/ does not exist, try dnf (RHEL)
if err != nil {
autoConf, err := cond{
Expand Down
129 changes: 61 additions & 68 deletions configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"reflect"

"github.com/BurntSushi/toml"
"github.com/safinsingh/aeaconf2"
"github.com/safinsingh/aeaconf2/compat"
)

// parseConfig takes the config content as a string and attempts to parse it
Expand All @@ -17,17 +19,26 @@ func parseConfig(configContent string) {
fail("Configuration is empty!")
os.Exit(1)
}
md, err := toml.Decode(configContent, &conf)

headerRaw, checksRaw, err := compat.SeparateConfig([]byte(configContent))
if err != nil {
fail("Error decoding TOML: " + err.Error())
fail("error separating config file: " + err.Error())
os.Exit(1)
}
if verboseEnabled {
for _, undecoded := range md.Undecoded() {
warn("Undecoded scoring configuration key \"" + undecoded.String() + "\" will not be used.")
}

cfg := new(config)
err = toml.Unmarshal(headerRaw, cfg)
if err != nil {
fail("error parsing config file header: " + err.Error())
os.Exit(1)
}

ab := aeaconf2.DefaultAeaconfBuilder(checksRaw, funcRegistry).
SetLineOffset(countLines(headerRaw)).
SetMaxPoints(cfg.MaxPoints)

cfg.Checks = ab.GetChecks()

// If there's no remote, local must be enabled.
if conf.Remote == "" {
conf.Local = true
Expand Down Expand Up @@ -62,19 +73,6 @@ func parseConfig(configContent string) {
info("Consider updating your config to include:")
info(" version = '" + version + "'")
}

// Print warnings for impossible checks and undefined check types.
for i, check := range conf.Check {
if len(check.Pass) == 0 && len(check.PassOverride) == 0 {
warn("Check " + fmt.Sprintf("%d", i+1) + " does not define any possible ways to pass!")
}
allConditions := append(append(append([]cond{}, check.Pass[:]...), check.Fail[:]...), check.PassOverride[:]...)
for j, cond := range allConditions {
if cond.Type == "" {
warn("Check " + fmt.Sprintf("%d condition %d", i+1, j+1) + " does not have a check type!")
}
}
}
}

// writeConfig writes the in-memory config to disk as the an encrypted
Expand Down Expand Up @@ -157,21 +155,8 @@ func printConfig() {
if conf.EndDate != "" {
pass("End Date:", conf.EndDate)
}
for i, check := range conf.Check {
green("CHCK", fmt.Sprintf("Check %d (%d points):", i+1, check.Points))
fmt.Println("Message:", check.Message)
for _, c := range check.Pass {
fmt.Println("Pass Condition:")
fmt.Print(c)
}
for _, c := range check.PassOverride {
fmt.Println("PassOverride Condition:")
fmt.Print(c)
}
for _, c := range check.Fail {
fmt.Println("Fail Condition:")
fmt.Print(c)
}
for i, check := range conf.Checks {
green("CHCK", fmt.Sprintf("Check %d: %s", i+1, check.Debug()))
}
}

Expand All @@ -182,64 +167,72 @@ func obfuscateConfig() {
if err := obfuscateData(&conf.Password); err != nil {
fail(err.Error())
}
for i, check := range conf.Check {
if err := obfuscateData(&conf.Check[i].Message); err != nil {
for i := range conf.Checks {
if err := obfuscateData(&conf.Checks[i].Message); err != nil {
fail(err.Error())
}
if conf.Check[i].Hint != "" {
if err := obfuscateData(&conf.Check[i].Hint); err != nil {
fail(err.Error())
}
}
for j := range check.Pass {
if err := obfuscateCond(&conf.Check[i].Pass[j]); err != nil {
if conf.Checks[i].Hint != "" {
if err := obfuscateData(&conf.Checks[i].Hint); err != nil {
fail(err.Error())
}
}
for j := range check.PassOverride {
if err := obfuscateCond(&conf.Check[i].PassOverride[j]); err != nil {
fail(err.Error())
}
}
for j := range check.Fail {
if err := obfuscateCond(&conf.Check[i].Fail[j]); err != nil {
fail(err.Error())
}
if err := obfuscateCond(conf.Checks[i].Condition); err != nil {
fail(err.Error())
}
}
}

// obfuscateCond is a convenience function to obfuscate all string fields of a
// struct using reflection. It assumes all struct fields are strings.
func obfuscateCond(c *cond) error {

// ummmmmm
func obfuscateCond(c aeaconf2.Condition) error {
s := reflect.ValueOf(c).Elem()
t := s.Type()

for i := 0; i < s.NumField(); i++ {
if s.Type().Field(i).Name == "regex" {
continue
}
datum := s.Field(i).String()
if err := obfuscateData(&datum); err != nil {
return err
field := s.Field(i)
fieldType := t.Field(i).Type

if fieldType.Kind() == reflect.String {
datum := field.String()
if err := obfuscateData(&datum); err != nil {
return err
}
field.SetString(datum)
} else if fieldType.Implements(reflect.TypeOf((*aeaconf2.Condition)(nil)).Elem()) {
if err := obfuscateCond(field.Addr().Interface().(aeaconf2.Condition)); err != nil {
return err
}
}
s.Field(i).SetString(datum)
}

return nil
}

// deobfuscateCond is a convenience function to deobfuscate all string fields
// of a struct using reflection.
func deobfuscateCond(c *cond) error {
func deobfuscateCond(c aeaconf2.Condition) error {
s := reflect.ValueOf(c).Elem()
t := s.Type()

for i := 0; i < s.NumField(); i++ {
if s.Type().Field(i).Name == "regex" {
continue
}
datum := s.Field(i).String()
if err := deobfuscateData(&datum); err != nil {
return err
field := s.Field(i)
fieldType := t.Field(i).Type

if fieldType.Kind() == reflect.String {
datum := field.String()
if err := deobfuscateData(&datum); err != nil {
return err
}
field.SetString(datum)
} else if fieldType.Implements(reflect.TypeOf((*aeaconf2.Condition)(nil)).Elem()) {
if err := deobfuscateCond(field.Addr().Interface().(aeaconf2.Condition)); err != nil {
return err
}
}
s.Field(i).SetString(datum)
}

return nil
}

Expand Down
Loading

0 comments on commit 990bc25

Please sign in to comment.