scan_common.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. package scan
  2. import (
  3. "crypto/x509"
  4. "net"
  5. "net/http"
  6. "regexp"
  7. "sync"
  8. "time"
  9. "github.com/cloudflare/cfssl/helpers"
  10. "github.com/cloudflare/cfssl/log"
  11. "github.com/cloudflare/cfssl/scan/crypto/tls"
  12. )
  13. var (
  14. // Network is the default network to use.
  15. Network = "tcp"
  16. // Dialer is the default dialer to use, with a 1s timeout.
  17. Dialer = &net.Dialer{Timeout: time.Second}
  18. // Client is the default HTTP Client.
  19. Client = &http.Client{Transport: &http.Transport{Dial: Dialer.Dial}}
  20. // RootCAs defines the default root certificate authorities to be used for scan.
  21. RootCAs *x509.CertPool
  22. )
  23. // Grade gives a subjective rating of the host's success in a scan.
  24. type Grade int
  25. const (
  26. // Bad describes a host with serious misconfiguration or vulnerability.
  27. Bad Grade = iota
  28. // Warning describes a host with non-ideal configuration that maintains support for Warning clients.
  29. Warning
  30. // Good describes host performing the expected state-of-the-art.
  31. Good
  32. // Skipped descibes the "grade" of a scan that has been skipped.
  33. Skipped
  34. )
  35. // String gives the name of the Grade as a string.
  36. func (g Grade) String() string {
  37. switch g {
  38. case Bad:
  39. return "Bad"
  40. case Warning:
  41. return "Warning"
  42. case Good:
  43. return "Good"
  44. case Skipped:
  45. return "Skipped"
  46. default:
  47. return "Invalid"
  48. }
  49. }
  50. // Output is the result of a scan, to be stored for potential use by later Scanners.
  51. type Output interface{}
  52. // multiscan scans all DNS addresses returned for the host, returning the lowest grade
  53. // and the concatenation of all the output.
  54. func multiscan(host string, scan func(string) (Grade, Output, error)) (grade Grade, output Output, err error) {
  55. domain, port, _ := net.SplitHostPort(host)
  56. var addrs []string
  57. addrs, err = net.LookupHost(domain)
  58. if err != nil {
  59. return
  60. }
  61. grade = Good
  62. out := make(map[string]Output)
  63. for _, addr := range addrs {
  64. var g Grade
  65. var o Output
  66. g, o, err = scan(net.JoinHostPort(addr, port))
  67. if err != nil {
  68. grade = Bad
  69. return
  70. }
  71. if g < grade {
  72. grade = g
  73. }
  74. out[addr] = o
  75. }
  76. output = out
  77. return
  78. }
  79. // Scanner describes a type of scan to perform on a host.
  80. type Scanner struct {
  81. // Description describes the nature of the scan to be performed.
  82. Description string `json:"description"`
  83. // scan is the function that scans the given host and provides a Grade and Output.
  84. scan func(string, string) (Grade, Output, error)
  85. }
  86. // Scan performs the scan to be performed on the given host and stores its result.
  87. func (s *Scanner) Scan(addr, hostname string) (Grade, Output, error) {
  88. grade, output, err := s.scan(addr, hostname)
  89. if err != nil {
  90. log.Debugf("scan: %v", err)
  91. return grade, output, err
  92. }
  93. return grade, output, err
  94. }
  95. // Family defines a set of related scans meant to be run together in sequence.
  96. type Family struct {
  97. // Description gives a short description of the scans performed scan/scan_common.goon the host.
  98. Description string `json:"description"`
  99. // Scanners is a list of scanners that are to be run in sequence.
  100. Scanners map[string]*Scanner `json:"scanners"`
  101. }
  102. // FamilySet contains a set of Families to run Scans from.
  103. type FamilySet map[string]*Family
  104. // Default contains each scan Family that is defined
  105. var Default = FamilySet{
  106. "Connectivity": Connectivity,
  107. "TLSHandshake": TLSHandshake,
  108. "TLSSession": TLSSession,
  109. "PKI": PKI,
  110. "Broad": Broad,
  111. }
  112. // ScannerResult contains the result for a single scan.
  113. type ScannerResult struct {
  114. Grade string `json:"grade"`
  115. Output Output `json:"output,omitempty"`
  116. Error string `json:"error,omitempty"`
  117. }
  118. // FamilyResult contains a scan response for a single Family
  119. type FamilyResult map[string]ScannerResult
  120. // A Result contains a ScannerResult along with it's scanner and family names.
  121. type Result struct {
  122. Family, Scanner string
  123. ScannerResult
  124. }
  125. type context struct {
  126. sync.WaitGroup
  127. addr, hostname string
  128. familyRegexp, scannerRegexp *regexp.Regexp
  129. resultChan chan *Result
  130. }
  131. func newContext(addr, hostname string, familyRegexp, scannerRegexp *regexp.Regexp, numFamilies int) *context {
  132. ctx := &context{
  133. addr: addr,
  134. hostname: hostname,
  135. familyRegexp: familyRegexp,
  136. scannerRegexp: scannerRegexp,
  137. resultChan: make(chan *Result),
  138. }
  139. ctx.Add(numFamilies)
  140. go func() {
  141. ctx.Wait()
  142. close(ctx.resultChan)
  143. }()
  144. return ctx
  145. }
  146. type familyContext struct {
  147. sync.WaitGroup
  148. ctx *context
  149. }
  150. func (ctx *context) newfamilyContext(numScanners int) *familyContext {
  151. familyCtx := &familyContext{ctx: ctx}
  152. familyCtx.Add(numScanners)
  153. go func() {
  154. familyCtx.Wait()
  155. familyCtx.ctx.Done()
  156. }()
  157. return familyCtx
  158. }
  159. func (ctx *context) copyResults(timeout time.Duration) map[string]FamilyResult {
  160. results := make(map[string]FamilyResult)
  161. for {
  162. var result *Result
  163. select {
  164. case <-time.After(timeout):
  165. log.Warningf("Scan timed out after %v", timeout)
  166. return results
  167. case result = <-ctx.resultChan:
  168. if result == nil {
  169. return results
  170. }
  171. }
  172. if results[result.Family] == nil {
  173. results[result.Family] = make(FamilyResult)
  174. }
  175. results[result.Family][result.Scanner] = result.ScannerResult
  176. }
  177. }
  178. func (familyCtx *familyContext) runScanner(familyName, scannerName string, scanner *Scanner) {
  179. if familyCtx.ctx.familyRegexp.MatchString(familyName) && familyCtx.ctx.scannerRegexp.MatchString(scannerName) {
  180. grade, output, err := scanner.Scan(familyCtx.ctx.addr, familyCtx.ctx.hostname)
  181. result := &Result{
  182. familyName,
  183. scannerName,
  184. ScannerResult{
  185. Grade: grade.String(),
  186. Output: output,
  187. },
  188. }
  189. if err != nil {
  190. result.Error = err.Error()
  191. }
  192. familyCtx.ctx.resultChan <- result
  193. }
  194. familyCtx.Done()
  195. }
  196. // RunScans iterates over AllScans, running each scan that matches the family
  197. // and scanner regular expressions concurrently.
  198. func (fs FamilySet) RunScans(host, ip, family, scanner string, timeout time.Duration) (map[string]FamilyResult, error) {
  199. hostname, port, err := net.SplitHostPort(host)
  200. if err != nil {
  201. hostname = host
  202. port = "443"
  203. }
  204. var addr string
  205. if net.ParseIP(ip) != nil {
  206. addr = net.JoinHostPort(ip, port)
  207. } else {
  208. addr = net.JoinHostPort(hostname, port)
  209. }
  210. familyRegexp, err := regexp.Compile(family)
  211. if err != nil {
  212. return nil, err
  213. }
  214. scannerRegexp, err := regexp.Compile(scanner)
  215. if err != nil {
  216. return nil, err
  217. }
  218. ctx := newContext(addr, hostname, familyRegexp, scannerRegexp, len(fs))
  219. for familyName, family := range fs {
  220. familyCtx := ctx.newfamilyContext(len(family.Scanners))
  221. for scannerName, scanner := range family.Scanners {
  222. go familyCtx.runScanner(familyName, scannerName, scanner)
  223. }
  224. }
  225. return ctx.copyResults(timeout), nil
  226. }
  227. // LoadRootCAs loads the default root certificate authorities from file.
  228. func LoadRootCAs(caBundleFile string) (err error) {
  229. if caBundleFile != "" {
  230. log.Debugf("Loading scan RootCAs: %s", caBundleFile)
  231. RootCAs, err = helpers.LoadPEMCertPool(caBundleFile)
  232. }
  233. return
  234. }
  235. func defaultTLSConfig(hostname string) *tls.Config {
  236. return &tls.Config{
  237. ServerName: hostname,
  238. RootCAs: RootCAs,
  239. InsecureSkipVerify: true,
  240. }
  241. }