func main() { timeout := flag.Duration("timeout", 5*time.Second, "Timeout after sending heartbeat") pg := flag.Bool("pg", false, "run a check specific to Postgres TLS") flag.Usage = func() { fmt.Printf("Usage: %s [options] host[:443]\n", os.Args[0]) fmt.Println("Options:") flag.PrintDefaults() } flag.Parse() host := flag.Arg(0) if !strings.Contains(host, ":") { if *pg { host += ":5432" } else { host += ":443" } } var c *tls.Conn var err error if *pg { c, err = pgStartTLS(host) } else { c, err = tls.Dial("tcp", host, &defaultTLSConfig) } if err != nil { log.Printf("Error connecting to %s: %s\n", host, err) os.Exit(2) } err = c.WriteHeartbeat(1, nil) if err == tls.ErrNoHeartbeat { fmt.Printf("SECURE - %s does not have the heartbeat extension enabled\n", host) os.Exit(0) } if err != nil { fmt.Println("UNKNOWN - Heartbeat enabled, but there was an error writing the payload:", err) os.Exit(2) } readErr := make(chan error) go func() { _, _, err := c.ReadHeartbeat() readErr <- err }() select { case err := <-readErr: if err == nil { fmt.Printf("VULNERABLE - %s has the heartbeat extension enabled and is vulnerable to CVE-2014-0160\n", host) os.Exit(1) } fmt.Printf("SECURE - %s has heartbeat extension enabled but is not vulnerable\n", host) fmt.Printf("This error happened while reading the response to the malformed heartbeat (almost certainly a good thing): %q\n", err) case <-time.After(*timeout): fmt.Printf("SECURE - %s has the heartbeat extension enabled, but timed out after a malformed heartbeat (this likely means that it is not vulnerable)\n", host) } }
func checkHeartbeat(host string, timeout time.Duration, dial Dialer) (int, error) { var err error var c *tls.Conn if dial != nil { c, err = dial(host) } else { c, err = tls.Dial("tcp", host, &defaultTLSConfig) } if err != nil { log.Printf("Error connecting to %s: %s\n", host, err) return ResultConnectionRefused, err } defer c.Close() err = c.WriteHeartbeat(1, nil) if err == tls.ErrNoHeartbeat { log.Printf("SECURE(%s) - does not have the heartbeat extension enabled", host) return ResultSecure, err } if err != nil { log.Printf("UNKNOWN(%s) - Heartbeat enabled, but there was an error writing the payload:", host, err) return ResultError, err } readErr := make(chan error) go func() { _, _, err := c.ReadHeartbeat() readErr <- err }() select { case err := <-readErr: if err == nil { log.Printf("VULNERABLE(%s) - has the heartbeat extension enabled and is vulnerable to CVE-2014-0160", host) return ResultVunerable, err } log.Printf("SECURE(%s) has heartbeat extension enabled but is not vulnerable: %q", host, err) return ResultSecure, err case <-time.After(timeout): } log.Printf("SECURE(%s) - has the heartbeat extension enabled, but timed out after a malformed heartbeat (this likely means that it is not vulnerable)", host) return ResultSecure, err }