@@ -13,8 +13,10 @@ import (
1313 "time"
1414
1515 "github.com/cockroachdb/errors"
16+ "github.com/rs/zerolog/log"
1617 "go.mondoo.com/cnquery/v11/llx"
1718 "go.mondoo.com/cnquery/v11/providers-sdk/v1/plugin"
19+ "go.mondoo.com/cnquery/v11/providers-sdk/v1/util/convert"
1820 "go.mondoo.com/cnquery/v11/providers/core/resources/regex"
1921 "go.mondoo.com/cnquery/v11/providers/network/connection"
2022 "go.mondoo.com/cnquery/v11/providers/network/resources/certificates"
@@ -26,6 +28,8 @@ var reTarget = regexp.MustCompile("([^/:]+?)(:\\d+)?$")
2628
2729var rexUrlDomain = regexp .MustCompile (regex .UrlDomain )
2830
31+ var DefaultDialerTimeout = time .Minute * 1
32+
2933// Returns the connection's port adjusted for TLS.
3034// If no port is set, we estimate what it might be from the scheme.
3135// If that doesn't help, we set it to 443.
@@ -108,7 +112,15 @@ func initTls(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]
108112}
109113
110114type mqlTlsInternal struct {
115+ // This mutex is used to protect the tls resource from doing multiple dections at once
111116 lock sync.Mutex
117+ // we only detect once if the a socket is running on TLS or not, once the detection runs,
118+ // this boolean gets set and tells other functions if the socket has tls enabled or not
119+ tlsEnabled * bool
120+ // if the socket has TLS enabled, this tester will have findings, ciphers and versions
121+ tester * tlsshake.Tester
122+ // during TLS detection, if we find any issue, we record it here
123+ Error error
112124}
113125
114126func (s * mqlTls ) id () (string , error ) {
@@ -185,24 +197,20 @@ func parseCertificates(runtime *plugin.Runtime, domainName string, certificateLi
185197}
186198
187199func (s * mqlTls ) params (socket * mqlSocket , domainName string ) (map [string ]interface {}, error ) {
188- s .lock .Lock ()
189- defer s .lock .Unlock ()
190-
191- host := socket .Address .Data
192- port := socket .Port .Data
193- proto := socket .Protocol .Data
194-
195- tester := tlsshake .New (proto , domainName , host , int (port ))
196- if err := tester .Test (tlsshake .DefaultScanConfig ()); err != nil {
197- if errors .Is (err , tlsshake .ErrFailedToConnect ) || errors .Is (err , tlsshake .ErrFailedToTlsResponse ) {
198- s .Params .State = plugin .StateIsSet | plugin .StateIsNull
199- return nil , nil
200- }
200+ enabled , err := s .TLSEnabled (socket , domainName )
201+ if err != nil {
201202 return nil , err
202203 }
203204
205+ if ! enabled {
206+ return nil , nil
207+ }
208+
209+ s .lock .Lock ()
210+ defer s .lock .Unlock ()
211+
204212 res := map [string ]interface {}{}
205- findings := tester .Findings
213+ findings := s . tester .Findings
206214
207215 lists := map [string ][]string {
208216 "errors" : findings .Errors ,
@@ -303,9 +311,16 @@ func (s *mqlTls) extensions(params interface{}) ([]interface{}, error) {
303311 return res , nil
304312}
305313
306- func gatherTlsCertificates (host , port , domainName string ) ([]* x509.Certificate , []* x509.Certificate , error ) {
314+ func gatherTlsCertificates (proto , host , port , domainName string ) ([]* x509.Certificate , []* x509.Certificate , error ) {
307315 isSNIcert := map [string ]struct {}{}
308- conn , err := tls .Dial ("tcp" , net .JoinHostPort (host , port ), & tls.Config {
316+ dialer := & net.Dialer {Timeout : DefaultDialerTimeout }
317+ addr := net .JoinHostPort (host , port )
318+ log .Trace ().
319+ Str ("address" , addr ).
320+ Str ("domain_name" , domainName ).
321+ Dur ("timeout" , DefaultDialerTimeout ).
322+ Msg ("network.tls> gathering tls certificates" )
323+ conn , err := tls .DialWithDialer (dialer , proto , addr , & tls.Config {
309324 InsecureSkipVerify : true ,
310325 ServerName : domainName ,
311326 })
@@ -321,7 +336,7 @@ func gatherTlsCertificates(host, port, domainName string) ([]*x509.Certificate,
321336 }
322337
323338 nonSniCerts := []* x509.Certificate {}
324- nonSniConn , err := tls .Dial ( "tcp" , net . JoinHostPort ( host , port ) , & tls.Config {
339+ nonSniConn , err := tls .DialWithDialer ( dialer , proto , addr , & tls.Config {
325340 InsecureSkipVerify : true ,
326341 })
327342 if err != nil {
@@ -338,11 +353,93 @@ func gatherTlsCertificates(host, port, domainName string) ([]*x509.Certificate,
338353 return sniCerts , nonSniCerts , nil
339354}
340355
356+ // we should only detect once if the socket is running on TLS or not, if we have already detected it and, it
357+ // is NOT a TLS connection, we should exit fast.
358+ //
359+ // NOTE that this method should be called by functions once they have locked the Mutex inside `mqlTlsInternal`
360+ func (s * mqlTls ) unsafeTLSTest (socket * mqlSocket , domainName string ) error {
361+ if s .tlsEnabled != nil {
362+ return s .Error
363+ }
364+
365+ host := socket .Address .Data
366+ port := socket .Port .Data
367+ proto := socket .Protocol .Data
368+
369+ s .tester = tlsshake .New (proto , domainName , host , int (port ))
370+ if err := s .tester .Test (tlsshake .DefaultScanConfig ()); err != nil {
371+
372+ log .Debug ().
373+ Str ("host" , host ).
374+ Str ("proto" , proto ).
375+ Int64 ("port" , port ).
376+ Interface ("findings" , s .tester .Findings ).
377+ Bool ("tls_enabled" , false ).
378+ Msg ("network.tls> detection" )
379+ s .tlsEnabled = convert .ToPtr (false )
380+
381+ if errors .Is (err , tlsshake .ErrFailedToConnect ) ||
382+ errors .Is (err , tlsshake .ErrFailedToWrite ) ||
383+ errors .Is (err , tlsshake .ErrTimeout ) ||
384+ errors .Is (err , tlsshake .ErrFailedToTlsResponse ) {
385+
386+ s .Params .State = plugin .StateIsSet | plugin .StateIsNull
387+ s .Certificates .State = plugin .StateIsSet | plugin .StateIsNull
388+ s .NonSniCertificates .State = plugin .StateIsSet | plugin .StateIsNull
389+ return nil
390+ }
391+
392+ s .Error = err
393+ return s .Error
394+ }
395+
396+ s .tlsEnabled = convert .ToPtr (len (s .tester .Findings .Versions ) != 0 )
397+
398+ log .Debug ().
399+ Str ("host" , host ).
400+ Str ("proto" , proto ).
401+ Int64 ("port" , port ).
402+ Strs ("versions" , s .tester .Findings .Errors ).
403+ Interface ("versions" , s .tester .Findings .Versions ).
404+ Bool ("tls_enabled" , * s .tlsEnabled ).
405+ Msg ("network.tls> detection" )
406+ return nil
407+ }
408+
409+ // TLSEnabled checks if the provider socket speaks TLS or plain text (like HTTP)
410+ func (s * mqlTls ) TLSEnabled (socket * mqlSocket , domainName string ) (enabled bool , err error ) {
411+ s .lock .Lock ()
412+ defer s .lock .Unlock ()
413+
414+ if s .tlsEnabled == nil {
415+ // we only detect once
416+ err = s .unsafeTLSTest (socket , domainName )
417+ } else {
418+ err = s .Error
419+ }
420+
421+ enabled = * s .tlsEnabled
422+
423+ return
424+ }
341425func (s * mqlTls ) populateCertificates (socket * mqlSocket , domainName string ) error {
426+ enabled , err := s .TLSEnabled (socket , domainName )
427+ if err != nil {
428+ return err
429+ }
430+
431+ if ! enabled {
432+ return nil
433+ }
434+
435+ s .lock .Lock ()
436+ defer s .lock .Unlock ()
437+
342438 host := socket .Address .Data
343439 port := socket .Port .Data
440+ proto := socket .Protocol .Data
344441
345- certs , nonSniCerts , err := gatherTlsCertificates (host , strconv .FormatInt (port , 10 ), domainName )
442+ certs , nonSniCerts , err := gatherTlsCertificates (proto , host , strconv .FormatInt (port , 10 ), domainName )
346443 if err != nil {
347444 s .Certificates = plugin.TValue [[]interface {}]{Error : err , State : plugin .StateIsSet }
348445 s .NonSniCertificates = plugin.TValue [[]interface {}]{Error : err , State : plugin .StateIsSet }
0 commit comments