From fdd1078e18d71553a723ac8e7788b6ccf2cdf24a Mon Sep 17 00:00:00 2001 From: Stuart Date: Tue, 24 May 2016 13:23:06 -0400 Subject: [PATCH 01/15] goimports --- osxlockdown.go | 91 ++++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/osxlockdown.go b/osxlockdown.go index 68373f8..0411bbe 100644 --- a/osxlockdown.go +++ b/osxlockdown.go @@ -9,11 +9,12 @@ import ( "path/filepath" "strings" "time" - "gopkg.in/yaml.v2" + + "gopkg.in/yaml.v2" ) // Version of osxlockdown -var Version = "0.9" +var Version = "0.9" // ReadFile takes a relative path and returns the bytes in that file func ReadFile(filename string) (data []byte, err error) { @@ -43,11 +44,11 @@ var ConfigRules ConfigRuleList // ConfigRule is a container for each individual rule type ConfigRule struct { - Title string `yaml:"title"` - CheckCommand string `yaml:"check_command"` - FixCommand string `yaml:"fix_command"` - Enabled bool `yaml:"enabled"` - AllowRemediation *bool `yaml:"allow_remediation"` + Title string `yaml:"title"` + CheckCommand string `yaml:"check_command"` + FixCommand string `yaml:"fix_command"` + Enabled bool `yaml:"enabled"` + AllowRemediation *bool `yaml:"allow_remediation"` } // ConfigRuleList is an array @@ -99,7 +100,9 @@ func GetSystemInfo() (sysinfo SystemInfo) { // CalculateScore returns the compliance score for this system func CalculateScore(ruleCount int, failCount int) int { - if ruleCount == 0 { return 0} + if ruleCount == 0 { + return 0 + } return int(float64(ruleCount-failCount) / float64(ruleCount) * 100.0) } @@ -121,7 +124,7 @@ func main() { flag.Parse() // Print the script's version and exit - if(*version) { + if *version { fmt.Printf("osxlockdown %s\n", Version) return } @@ -144,49 +147,49 @@ func main() { ruleCount := 0 failCount := 0 - for _, rule := range ConfigRules { - if rule.Enabled { - checkCommand := rule.CheckCommand - ruleCount++ - - result := RunCommand(checkCommand) - - resultText := "\033[32mPASSED\033[39m" - if !result { - // Audit failed, check if we can remediate - if *remediate && AllowRemediation(rule) { - // Remediate - fixCommand := rule.FixCommand - RunCommand(fixCommand) - // Check our fix worked - result = RunCommand(checkCommand) - if result { - resultText = "\033[34mFIXED \033[39m" - } - } - - if !result { - failCount++ - resultText = "\033[31mFAILED\033[39m" - } - } - - if !result || !*hidePasses { - fmt.Printf("[%s] %s\n", resultText, rule.Title) - } - } - } - + for _, rule := range ConfigRules { + if rule.Enabled { + checkCommand := rule.CheckCommand + ruleCount++ + + result := RunCommand(checkCommand) + + resultText := "\033[32mPASSED\033[39m" + if !result { + // Audit failed, check if we can remediate + if *remediate && AllowRemediation(rule) { + // Remediate + fixCommand := rule.FixCommand + RunCommand(fixCommand) + // Check our fix worked + result = RunCommand(checkCommand) + if result { + resultText = "\033[34mFIXED \033[39m" + } + } + + if !result { + failCount++ + resultText = "\033[31mFAILED\033[39m" + } + } + + if !result || !*hidePasses { + fmt.Printf("[%s] %s\n", resultText, rule.Title) + } + } + } + // Print summary if !*hideSummary { fmt.Printf("-------------------------------------------------------------------------------\n") - fmt.Printf("osxlockdown %s\n", Version) + fmt.Printf("osxlockdown %s\n", Version) t := time.Now() fmt.Printf("Date: %s\n", t.Format("2006-01-02T15:04:05-07:00")) sysinfo := GetSystemInfo() fmt.Printf("SerialNumber: %s\nHardwareUUID: %s\n", sysinfo.SerialNumber, sysinfo.HardwareUUID) fmt.Printf("Final Score %d%%; Pass rate: %d/%d\n", CalculateScore(ruleCount, failCount), - (ruleCount-failCount), ruleCount) + (ruleCount - failCount), ruleCount) } } From 686ba1ef0f75426c73876f99a27d841780dffca4 Mon Sep 17 00:00:00 2001 From: Stuart Date: Tue, 24 May 2016 13:24:43 -0400 Subject: [PATCH 02/15] commands_file like other flags --- osxlockdown.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osxlockdown.go b/osxlockdown.go index 0411bbe..3d4e9e2 100644 --- a/osxlockdown.go +++ b/osxlockdown.go @@ -117,9 +117,7 @@ func main() { hidePasses := flag.Bool("hide_passes", false, "Disables printing the rules that passed") remediate := flag.Bool("remediate", false, "Implements fixes for failed checks. WARNING: Beware this may break things.") version := flag.Bool("version", false, "Prints the script's version and exits") - - var commandFile string - flag.StringVar(&commandFile, "commands_file", "commands.yaml", "YAML file containing the commands and configuration") + commandFile := flag.String("commands_file", "commands.yaml", "YAML file containing the commands and configuration") flag.Parse() @@ -137,7 +135,7 @@ func main() { } // Read our command/config file - err := ReadConfigRules(commandFile) + err := ReadConfigRules(*commandFile) if err != nil { fmt.Println(err) return From ee3298009864b6d5075ae8081bd85517f8e4d7eb Mon Sep 17 00:00:00 2001 From: Stuart Date: Tue, 24 May 2016 13:33:58 -0400 Subject: [PATCH 03/15] Don't assume commands work --- osxlockdown.go | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/osxlockdown.go b/osxlockdown.go index 3d4e9e2..81a5b1e 100644 --- a/osxlockdown.go +++ b/osxlockdown.go @@ -85,17 +85,20 @@ type SystemInfo struct { } // GetCommandOutput runs a command and returns it's output -func GetCommandOutput(cmd string) string { - out, _ := exec.Command("bash", "-c", cmd).Output() - return strings.TrimSpace(string(out)) +func GetCommandOutput(cmd string) (string, error) { + out, err := exec.Command("bash", "-c", cmd).Output() + return strings.TrimSpace(string(out)), err } // GetSystemInfo collects information about the system -func GetSystemInfo() (sysinfo SystemInfo) { - sysinfo.SerialNumber = GetCommandOutput("system_profiler SPHardwareDataType | grep \"Serial Number\" | cut -d: -f2") - sysinfo.HardwareUUID = GetCommandOutput("system_profiler SPHardwareDataType | grep \"Hardware UUID\" | cut -d: -f2") - - return sysinfo +func GetSystemInfo() (sysinfo SystemInfo, err error) { + if sysinfo.SerialNumber, err = GetCommandOutput("system_profiler SPHardwareDataType | grep \"Serial Number\" | cut -d: -f2"); nil != err { + return + } + if sysinfo.HardwareUUID, err = GetCommandOutput("system_profiler SPHardwareDataType | grep \"Hardware UUID\" | cut -d: -f2"); nil != err { + return + } + return sysinfo, err } // CalculateScore returns the compliance score for this system @@ -128,14 +131,18 @@ func main() { } // Check OS version to make sure we will work - osVersion := GetCommandOutput("system_profiler SPSoftwareDataType | grep \"System Version\" | cut -d: -f2") + osVersion, err := GetCommandOutput("system_profiler SPSoftwareDataType | grep \"System Version\" | cut -d: -f2") + if nil != err { + fmt.Fprintf(os.Stderr, "Unable to determine OS Version: %v\nThis tool was meant to be used only on OS X 10.11 (El Capitan)\n", err) + return + } if !strings.Contains(osVersion, "OS X 10.11") { fmt.Println("ERROR: Unsupported OS. This tool was meant to be used only on OSX 10.11 (El Capitan)") return } // Read our command/config file - err := ReadConfigRules(*commandFile) + err = ReadConfigRules(*commandFile) if err != nil { fmt.Println(err) return @@ -184,8 +191,12 @@ func main() { fmt.Printf("osxlockdown %s\n", Version) t := time.Now() fmt.Printf("Date: %s\n", t.Format("2006-01-02T15:04:05-07:00")) - sysinfo := GetSystemInfo() - fmt.Printf("SerialNumber: %s\nHardwareUUID: %s\n", sysinfo.SerialNumber, sysinfo.HardwareUUID) + sysinfo, err := GetSystemInfo() + if nil != err { + fmt.Printf("Unable to determine Serial Number or Hardware UUID: %v", err) + } else { + fmt.Printf("SerialNumber: %s\nHardwareUUID: %s\n", sysinfo.SerialNumber, sysinfo.HardwareUUID) + } fmt.Printf("Final Score %d%%; Pass rate: %d/%d\n", CalculateScore(ruleCount, failCount), (ruleCount - failCount), ruleCount) From cda0119cf3ced0c9d11f88d64821fe52175a5eab Mon Sep 17 00:00:00 2001 From: Stuart Date: Tue, 24 May 2016 13:35:44 -0400 Subject: [PATCH 04/15] Get stderr, in case only something early in the pipe fails --- osxlockdown.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osxlockdown.go b/osxlockdown.go index 81a5b1e..6f1074d 100644 --- a/osxlockdown.go +++ b/osxlockdown.go @@ -86,7 +86,7 @@ type SystemInfo struct { // GetCommandOutput runs a command and returns it's output func GetCommandOutput(cmd string) (string, error) { - out, err := exec.Command("bash", "-c", cmd).Output() + out, err := exec.Command("bash", "-c", cmd).CombinedOutput() return strings.TrimSpace(string(out)), err } From 03e8e3f31185f38f2db2ce5970a923fcd8f80440 Mon Sep 17 00:00:00 2001 From: Stuart Date: Tue, 24 May 2016 13:39:23 -0400 Subject: [PATCH 05/15] Make sure incompatibility errors go to stderr --- osxlockdown.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osxlockdown.go b/osxlockdown.go index 6f1074d..3ce4e25 100644 --- a/osxlockdown.go +++ b/osxlockdown.go @@ -132,12 +132,15 @@ func main() { // Check OS version to make sure we will work osVersion, err := GetCommandOutput("system_profiler SPSoftwareDataType | grep \"System Version\" | cut -d: -f2") + bad := "" if nil != err { - fmt.Fprintf(os.Stderr, "Unable to determine OS Version: %v\nThis tool was meant to be used only on OS X 10.11 (El Capitan)\n", err) - return + bad = fmt.Sprintf("ERROR: Unable to determine OS Version: %v\n", err) } if !strings.Contains(osVersion, "OS X 10.11") { - fmt.Println("ERROR: Unsupported OS. This tool was meant to be used only on OSX 10.11 (El Capitan)") + bad = "ERROR: Unsupported OS. " + } + if "" != bad { + fmt.Fprintf(os.Stderr, "%sThis tool was meant to be used only on OSX 10.11 (El Capitan)", bad) return } From 726c7ce726dc145bb8901bddb082c8eae987e520 Mon Sep 17 00:00:00 2001 From: Stuart Date: Tue, 24 May 2016 13:43:09 -0400 Subject: [PATCH 06/15] Let user know reading the config file failed --- osxlockdown.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osxlockdown.go b/osxlockdown.go index 3ce4e25..08a921b 100644 --- a/osxlockdown.go +++ b/osxlockdown.go @@ -147,7 +147,7 @@ func main() { // Read our command/config file err = ReadConfigRules(*commandFile) if err != nil { - fmt.Println(err) + fmt.Fprintf(os.Stderr, "Unable to read config file: %v\n", err) return } From 8a76b824e761bb25326420dd1c639df503bc908c Mon Sep 17 00:00:00 2001 From: Stuart Date: Tue, 24 May 2016 13:46:18 -0400 Subject: [PATCH 07/15] Prefer stdlib's ReadFile --- osxlockdown.go | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/osxlockdown.go b/osxlockdown.go index 08a921b..c59c8a5 100644 --- a/osxlockdown.go +++ b/osxlockdown.go @@ -6,7 +6,6 @@ import ( "io/ioutil" "os" "os/exec" - "path/filepath" "strings" "time" @@ -16,29 +15,6 @@ import ( // Version of osxlockdown var Version = "0.9" -// ReadFile takes a relative path and returns the bytes in that file -func ReadFile(filename string) (data []byte, err error) { - path, err := filepath.Abs(filename) - if err != nil { - fmt.Println("ERROR: Unable to file:", filename) - return nil, err - } - - filehandle, err := os.Open(path) - if err != nil { - fmt.Println("ERROR: Error opening file:", path) - return nil, err - } - defer filehandle.Close() - - data, err = ioutil.ReadAll(filehandle) - if err != nil { - return nil, err - } - - return data, nil -} - // ConfigRules holds our yaml file containing our config var ConfigRules ConfigRuleList @@ -56,7 +32,7 @@ type ConfigRuleList []ConfigRule // ReadConfigRules reads our yaml file func ReadConfigRules(configFile string) error { - ruleFile, err := ReadFile(configFile) + ruleFile, err := ioutil.ReadFile(configFile) if err != nil { return err } From 0d57c7f77a824144aa170a28df3eb09647c5498f Mon Sep 17 00:00:00 2001 From: Stuart Date: Tue, 24 May 2016 13:59:59 -0400 Subject: [PATCH 08/15] Move config rules to local variables --- osxlockdown.go | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/osxlockdown.go b/osxlockdown.go index c59c8a5..ddaf87b 100644 --- a/osxlockdown.go +++ b/osxlockdown.go @@ -15,9 +15,6 @@ import ( // Version of osxlockdown var Version = "0.9" -// ConfigRules holds our yaml file containing our config -var ConfigRules ConfigRuleList - // ConfigRule is a container for each individual rule type ConfigRule struct { Title string `yaml:"title"` @@ -27,21 +24,19 @@ type ConfigRule struct { AllowRemediation *bool `yaml:"allow_remediation"` } -// ConfigRuleList is an array -type ConfigRuleList []ConfigRule - // ReadConfigRules reads our yaml file -func ReadConfigRules(configFile string) error { +func ReadConfigRules(configFile string) ([]ConfigRule, error) { ruleFile, err := ioutil.ReadFile(configFile) if err != nil { - return err + return nil, err } - err = yaml.Unmarshal(ruleFile, &ConfigRules) + var crs []ConfigRule + err = yaml.Unmarshal(ruleFile, &crs) if err != nil { - return err + return nil, err } - return nil + return crs, nil } // RunCommand returns true if the audit passed, or command was successful @@ -121,7 +116,7 @@ func main() { } // Read our command/config file - err = ReadConfigRules(*commandFile) + ConfigRules, err := ReadConfigRules(*commandFile) if err != nil { fmt.Fprintf(os.Stderr, "Unable to read config file: %v\n", err) return From 38448d7179501548a6e96b3df70c0819189725b2 Mon Sep 17 00:00:00 2001 From: Stuart Date: Tue, 24 May 2016 14:17:20 -0400 Subject: [PATCH 09/15] Use continue to skip loop --- osxlockdown.go | 52 ++++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/osxlockdown.go b/osxlockdown.go index ddaf87b..0605a71 100644 --- a/osxlockdown.go +++ b/osxlockdown.go @@ -127,36 +127,38 @@ func main() { failCount := 0 for _, rule := range ConfigRules { - if rule.Enabled { - checkCommand := rule.CheckCommand - ruleCount++ - - result := RunCommand(checkCommand) - - resultText := "\033[32mPASSED\033[39m" - if !result { - // Audit failed, check if we can remediate - if *remediate && AllowRemediation(rule) { - // Remediate - fixCommand := rule.FixCommand - RunCommand(fixCommand) - // Check our fix worked - result = RunCommand(checkCommand) - if result { - resultText = "\033[34mFIXED \033[39m" - } - } - - if !result { - failCount++ - resultText = "\033[31mFAILED\033[39m" + // Skip disabled rules + if !rule.Enabled { + continue + } + checkCommand := rule.CheckCommand + ruleCount++ + + result := RunCommand(checkCommand) + + resultText := "\033[32mPASSED\033[39m" + if !result { + // Audit failed, check if we can remediate + if *remediate && AllowRemediation(rule) { + // Remediate + fixCommand := rule.FixCommand + RunCommand(fixCommand) + // Check our fix worked + result = RunCommand(checkCommand) + if result { + resultText = "\033[34mFIXED \033[39m" } } - if !result || !*hidePasses { - fmt.Printf("[%s] %s\n", resultText, rule.Title) + if !result { + failCount++ + resultText = "\033[31mFAILED\033[39m" } } + + if !result || !*hidePasses { + fmt.Printf("[%s] %s\n", resultText, rule.Title) + } } // Print summary From 666607afc85b5ffd96bf8de272812a8828203aa3 Mon Sep 17 00:00:00 2001 From: Stuart Date: Tue, 24 May 2016 14:22:59 -0400 Subject: [PATCH 10/15] Don't get output of ran command --- osxlockdown.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/osxlockdown.go b/osxlockdown.go index 0605a71..f4e2f8c 100644 --- a/osxlockdown.go +++ b/osxlockdown.go @@ -41,12 +41,7 @@ func ReadConfigRules(configFile string) ([]ConfigRule, error) { // RunCommand returns true if the audit passed, or command was successful func RunCommand(cmd string) bool { - _, err := exec.Command("bash", "-c", cmd).Output() - if err != nil { - return false - } - - return true + return nil == exec.Command("bash", "-c", cmd).Run() } // SystemInfo holds system information From eb4493a24c0af511546e62535e429055868f1383 Mon Sep 17 00:00:00 2001 From: Stuart Date: Tue, 24 May 2016 14:29:47 -0400 Subject: [PATCH 11/15] Un-hardcode constants --- osxlockdown.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/osxlockdown.go b/osxlockdown.go index f4e2f8c..384fedc 100644 --- a/osxlockdown.go +++ b/osxlockdown.go @@ -13,7 +13,14 @@ import ( ) // Version of osxlockdown -var Version = "0.9" +const Version = "0.9" + +// Color-coded messages +const ( + PASSED = "\033[32mPASSED\033[39m" + FIXED = "\033[34mFIXED \033[39m" + FAILED = "\033[31mFAILED\033[39m" +) // ConfigRule is a container for each individual rule type ConfigRule struct { @@ -131,7 +138,7 @@ func main() { result := RunCommand(checkCommand) - resultText := "\033[32mPASSED\033[39m" + resultText := PASSED if !result { // Audit failed, check if we can remediate if *remediate && AllowRemediation(rule) { @@ -141,13 +148,13 @@ func main() { // Check our fix worked result = RunCommand(checkCommand) if result { - resultText = "\033[34mFIXED \033[39m" + resultText = FIXED } } if !result { failCount++ - resultText = "\033[31mFAILED\033[39m" + resultText = FAILED } } From 04ed933a3212c4531469ecaeebf4ca31e4484701 Mon Sep 17 00:00:00 2001 From: Stuart Date: Tue, 24 May 2016 14:39:43 -0400 Subject: [PATCH 12/15] Make ShouldRemediate a struct method --- osxlockdown.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osxlockdown.go b/osxlockdown.go index 384fedc..e076588 100644 --- a/osxlockdown.go +++ b/osxlockdown.go @@ -31,6 +31,11 @@ type ConfigRule struct { AllowRemediation *bool `yaml:"allow_remediation"` } +// ShouldRemediate returns true if the rule is allowed to be remediated +func (c ConfigRule) ShouldRemediate() bool { + return c.FixCommand != "" && (c.AllowRemediation == nil || *c.AllowRemediation) +} + // ReadConfigRules reads our yaml file func ReadConfigRules(configFile string) ([]ConfigRule, error) { ruleFile, err := ioutil.ReadFile(configFile) @@ -82,11 +87,6 @@ func CalculateScore(ruleCount int, failCount int) int { return int(float64(ruleCount-failCount) / float64(ruleCount) * 100.0) } -// AllowRemediation returns true if the rule is allowed to be remediated -func AllowRemediation(configRule ConfigRule) bool { - return configRule.FixCommand != "" && (configRule.AllowRemediation == nil || *configRule.AllowRemediation) -} - func main() { hideSummary := flag.Bool("hide_summary", false, "Disables printing the summary") @@ -141,7 +141,7 @@ func main() { resultText := PASSED if !result { // Audit failed, check if we can remediate - if *remediate && AllowRemediation(rule) { + if *remediate && rule.ShouldRemediate() { // Remediate fixCommand := rule.FixCommand RunCommand(fixCommand) From 2ca42ee885ed5a8490a792e8a5ffe6c755cc65ff Mon Sep 17 00:00:00 2001 From: Stuart Date: Tue, 24 May 2016 15:22:00 -0400 Subject: [PATCH 13/15] Move functions to struct methods, rework main loop --- osxlockdown.go | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/osxlockdown.go b/osxlockdown.go index e076588..3e4b14d 100644 --- a/osxlockdown.go +++ b/osxlockdown.go @@ -36,6 +36,16 @@ func (c ConfigRule) ShouldRemediate() bool { return c.FixCommand != "" && (c.AllowRemediation == nil || *c.AllowRemediation) } +// Check returns true if the audit passed, or command was successful +func (c ConfigRule) Check() bool { + return nil == exec.Command("bash", "-c", c.CheckCommand).Run() +} + +// Remediate attempts to run the remediation command +func (c ConfigRule) Fix() ([]byte, error) { + return exec.Command("/bin/bash", "-c", c.FixCommand).CombinedOutput() +} + // ReadConfigRules reads our yaml file func ReadConfigRules(configFile string) ([]ConfigRule, error) { ruleFile, err := ioutil.ReadFile(configFile) @@ -51,11 +61,6 @@ func ReadConfigRules(configFile string) ([]ConfigRule, error) { return crs, nil } -// RunCommand returns true if the audit passed, or command was successful -func RunCommand(cmd string) bool { - return nil == exec.Command("bash", "-c", cmd).Run() -} - // SystemInfo holds system information type SystemInfo struct { SerialNumber string @@ -133,32 +138,29 @@ func main() { if !rule.Enabled { continue } - checkCommand := rule.CheckCommand + // Note we've found another rule ruleCount++ - result := RunCommand(checkCommand) - resultText := PASSED - if !result { + if !rule.Check() { + resultText = FAILED // Audit failed, check if we can remediate if *remediate && rule.ShouldRemediate() { - // Remediate - fixCommand := rule.FixCommand - RunCommand(fixCommand) - // Check our fix worked - result = RunCommand(checkCommand) - if result { + // Try to remediate + rule.Fix() + // Check if our fix worked + if rule.Check() { resultText = FIXED } } + } - if !result { - failCount++ - resultText = FAILED - } + // Note Failures + if FAILED == resultText { + failCount++ } - if !result || !*hidePasses { + if PASSED != resultText || !*hidePasses { fmt.Printf("[%s] %s\n", resultText, rule.Title) } } From 26135c78a5ba3bc00ac4e973d0ad39cab3f6dd90 Mon Sep 17 00:00:00 2001 From: Stuart Date: Tue, 24 May 2016 15:28:30 -0400 Subject: [PATCH 14/15] Typos; warn user about no (enabled) rules --- osxlockdown.go | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/osxlockdown.go b/osxlockdown.go index 3e4b14d..cc9cd30 100644 --- a/osxlockdown.go +++ b/osxlockdown.go @@ -118,7 +118,7 @@ func main() { bad = "ERROR: Unsupported OS. " } if "" != bad { - fmt.Fprintf(os.Stderr, "%sThis tool was meant to be used only on OSX 10.11 (El Capitan)", bad) + fmt.Fprintf(os.Stderr, "%sThis tool was meant to be used only on OSX 10.11 (El Capitan)\n", bad) return } @@ -129,6 +129,12 @@ func main() { return } + // Make sure we actually have rules + if 0 == len(ConfigRules) { + fmt.Fprintf(os.Stderr, "No rules found in conig file %v", *commandFile) + return + } + // Run commands and print results ruleCount := 0 failCount := 0 @@ -169,16 +175,20 @@ func main() { if !*hideSummary { fmt.Printf("-------------------------------------------------------------------------------\n") fmt.Printf("osxlockdown %s\n", Version) - t := time.Now() - fmt.Printf("Date: %s\n", t.Format("2006-01-02T15:04:05-07:00")) + fmt.Printf("Date: %s\n", time.Now().Format("2006-01-02T15:04:05-07:00")) sysinfo, err := GetSystemInfo() if nil != err { fmt.Printf("Unable to determine Serial Number or Hardware UUID: %v", err) } else { fmt.Printf("SerialNumber: %s\nHardwareUUID: %s\n", sysinfo.SerialNumber, sysinfo.HardwareUUID) } - fmt.Printf("Final Score %d%%; Pass rate: %d/%d\n", - CalculateScore(ruleCount, failCount), - (ruleCount - failCount), ruleCount) + /* Warn user if there was no actual checking */ + if 0 == ruleCount { + fmt.Printf("No enabled rules found in config file %v\n", *commandFile) + } else { + fmt.Printf("Final Score %d%%; Pass rate: %d/%d\n", + CalculateScore(ruleCount, failCount), + (ruleCount - failCount), ruleCount) + } } } From 09799f8ab44948e52766edf0a21700569cdf356a Mon Sep 17 00:00:00 2001 From: Stuart Date: Tue, 24 May 2016 16:18:56 -0400 Subject: [PATCH 15/15] Debugging output --- osxlockdown.go | 68 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/osxlockdown.go b/osxlockdown.go index cc9cd30..11df5c5 100644 --- a/osxlockdown.go +++ b/osxlockdown.go @@ -17,9 +17,10 @@ const Version = "0.9" // Color-coded messages const ( - PASSED = "\033[32mPASSED\033[39m" - FIXED = "\033[34mFIXED \033[39m" - FAILED = "\033[31mFAILED\033[39m" + PASSED = "\033[32mPASSED \033[39m" + FIXED = "\033[34mFIXED \033[39m" + FAILED = "\033[31mFAILED \033[39m" + SKIPPED = "\033[33mSKIPPED\033[39m" ) // ConfigRule is a container for each individual rule @@ -36,14 +37,43 @@ func (c ConfigRule) ShouldRemediate() bool { return c.FixCommand != "" && (c.AllowRemediation == nil || *c.AllowRemediation) } -// Check returns true if the audit passed, or command was successful -func (c ConfigRule) Check() bool { - return nil == exec.Command("bash", "-c", c.CheckCommand).Run() +// Check returns true if the audit passed, or command was successful. If +// debug is true, it will also print the command, any output, and any errors +// which may have occurred. +func (c ConfigRule) Check(debug bool) bool { + if debug { + fmt.Printf("==> Check: %s", c.CheckCommand) + } + return c.exec(c.CheckCommand, debug) + } -// Remediate attempts to run the remediation command -func (c ConfigRule) Fix() ([]byte, error) { - return exec.Command("/bin/bash", "-c", c.FixCommand).CombinedOutput() +// Fix tries to fix the issue, and then rechecks it with recheck. Debug has +// the same meaning as for Check(). The value returned is the vaule from +// Check(). +func (c ConfigRule) Fix(debug bool) bool { + if debug { + fmt.Printf("==> Fix: %s", c.FixCommand) + } + c.exec(c.FixCommand, debug) + return c.Check(debug) +} + +// exec runs cmd, and returns true if the command executed successfully. If +// debug is true, it will also print the command, any output, and any errors +// which may have occurred +func (c ConfigRule) exec(cmd string, debug bool) bool { + o, err := exec.Command("/bin/bash", "-c", cmd).CombinedOutput() + if debug { + if 0 != len(o) { + fmt.Printf("%s", o) + } + if nil != err { + fmt.Printf("Error: %v\n", err) + } + } + return nil == err + } // ReadConfigRules reads our yaml file @@ -99,9 +129,16 @@ func main() { remediate := flag.Bool("remediate", false, "Implements fixes for failed checks. WARNING: Beware this may break things.") version := flag.Bool("version", false, "Prints the script's version and exits") commandFile := flag.String("commands_file", "commands.yaml", "YAML file containing the commands and configuration") + debug := flag.Bool("debug", false, "Prints command output and other debugging messages") flag.Parse() + // Debugging output + pd := func(string, ...interface{}) (int, error) { return 0, nil } + if *debug { + pd = fmt.Printf + } + // Print the script's version and exit if *version { fmt.Printf("osxlockdown %s\n", Version) @@ -121,6 +158,7 @@ func main() { fmt.Fprintf(os.Stderr, "%sThis tool was meant to be used only on OSX 10.11 (El Capitan)\n", bad) return } + pd("Correct OS Version detected\n") // Read our command/config file ConfigRules, err := ReadConfigRules(*commandFile) @@ -131,9 +169,10 @@ func main() { // Make sure we actually have rules if 0 == len(ConfigRules) { - fmt.Fprintf(os.Stderr, "No rules found in conig file %v", *commandFile) + fmt.Fprintf(os.Stderr, "No rules found in config file %v", *commandFile) return } + pd("Read %v rules from %v", len(ConfigRules), *commandFile) // Run commands and print results ruleCount := 0 @@ -142,20 +181,21 @@ func main() { for _, rule := range ConfigRules { // Skip disabled rules if !rule.Enabled { + fmt.Printf("[%s] %s\n", SKIPPED, rule.Title) continue } + pd("=> %s\n", rule.Title) + // Note we've found another rule ruleCount++ resultText := PASSED - if !rule.Check() { + if !rule.Check(*debug) { resultText = FAILED // Audit failed, check if we can remediate if *remediate && rule.ShouldRemediate() { // Try to remediate - rule.Fix() - // Check if our fix worked - if rule.Check() { + if rule.Fix(*debug) { resultText = FIXED } }