func ExampleDial() { // Connecting with a custom root-certificate set. const rootPEM = ` -----BEGIN CERTIFICATE----- MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i YWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7 qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g K4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI KwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n ZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB BQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY /iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/ zG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6 yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx -----END CERTIFICATE-----` // First, create the set of root certificates. For this example we only // have one. It's also possible to omit this in order to use the // default root set of the current operating system. roots := x509.NewCertPool() ok := roots.AppendCertsFromPEM([]byte(rootPEM)) if !ok { panic("failed to parse root certificate") } conn, err := ztls.Dial("tcp", "mail.google.com:443", &ztls.Config{ RootCAs: roots, }) if err != nil { panic("failed to connect: " + err.Error()) } conn.Close() }
func ExampleCertificate_Verify() { // Verifying with a custom list of root certificates. const rootPEM = ` -----BEGIN CERTIFICATE----- MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i YWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7 qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g K4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI KwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n ZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB BQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY /iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/ zG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6 yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx -----END CERTIFICATE-----` const certPEM = ` -----BEGIN CERTIFICATE----- MIIDujCCAqKgAwIBAgIIE31FZVaPXTUwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl cm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwMTI5MTMyNzQzWhcNMTQwNTI5MDAwMDAw WjBpMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEYMBYGA1UEAwwPbWFp bC5nb29nbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfRrObuSW5T7q 5CnSEqefEmtH4CCv6+5EckuriNr1CjfVvqzwfAhopXkLrq45EQm8vkmf7W96XJhC 7ZM0dYi1/qOCAU8wggFLMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAa BgNVHREEEzARgg9tYWlsLmdvb2dsZS5jb20wCwYDVR0PBAQDAgeAMGgGCCsGAQUF BwEBBFwwWjArBggrBgEFBQcwAoYfaHR0cDovL3BraS5nb29nbGUuY29tL0dJQUcy LmNydDArBggrBgEFBQcwAYYfaHR0cDovL2NsaWVudHMxLmdvb2dsZS5jb20vb2Nz cDAdBgNVHQ4EFgQUiJxtimAuTfwb+aUtBn5UYKreKvMwDAYDVR0TAQH/BAIwADAf BgNVHSMEGDAWgBRK3QYWG7z2aLV29YG2u2IaulqBLzAXBgNVHSAEEDAOMAwGCisG AQQB1nkCBQEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3BraS5nb29nbGUuY29t L0dJQUcyLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAH6RYHxHdcGpMpFE3oxDoFnP+ gtuBCHan2yE2GRbJ2Cw8Lw0MmuKqHlf9RSeYfd3BXeKkj1qO6TVKwCh+0HdZk283 TZZyzmEOyclm3UGFYe82P/iDFt+CeQ3NpmBg+GoaVCuWAARJN/KfglbLyyYygcQq 0SgeDh8dRKUiaW3HQSoYvTvdTuqzwK4CXsr3b5/dAOY8uMuG/IAR3FgwTbZ1dtoW RvOTa8hYiU6A475WuZKyEHcwnGYe57u2I2KbMgcKjPniocj4QzgYsVAVKW3IwaOh yE+vPxsiUkvQHdO2fojCkY8jg70jxM+gu59tPDNbw3Uh/2Ij310FgTHsnGQMyA== -----END CERTIFICATE-----` // First, create the set of root certificates. For this example we only // have one. It's also possible to omit this in order to use the // default root set of the current operating system. roots := x509.NewCertPool() ok := roots.AppendCertsFromPEM([]byte(rootPEM)) if !ok { panic("failed to parse root certificate") } block, _ := pem.Decode([]byte(certPEM)) if block == nil { panic("failed to parse certificate PEM") } cert, err := x509.ParseCertificate(block.Bytes) if err != nil { panic("failed to parse certificate: " + err.Error()) } opts := x509.VerifyOptions{ DNSName: "mail.google.com", Roots: roots, } if _, err := cert.Verify(opts); err != nil { panic("failed to verify certificate: " + err.Error()) } }
// Pre-main bind flags to variables func init() { flag.StringVar(&config.Encoding, "encoding", "string", "Encode banner as string|hex|base64") flag.StringVar(&outputFileName, "output-file", "-", "Output filename, use - for stdout") flag.StringVar(&inputFileName, "input-file", "-", "Input filename, use - for stdin") flag.StringVar(&metadataFileName, "metadata-file", "-", "File to record banner-grab metadata, use - for stdout") flag.StringVar(&logFileName, "log-file", "-", "File to log to, use - for stderr") flag.StringVar(&interfaceName, "interface", "", "Network interface to send on") flag.UintVar(&portFlag, "port", 80, "Port to grab on") flag.UintVar(&timeout, "timeout", 10, "Set connection timeout in seconds") flag.BoolVar(&config.TLS, "tls", false, "Grab over TLS") flag.StringVar(&tlsVersion, "tls-version", "", "Max TLS version to use (implies --tls)") flag.BoolVar(&udp, "udp", false, "Grab over UDP") flag.UintVar(&config.Senders, "senders", 1000, "Number of send coroutines to use") flag.UintVar(&config.ConnectionsPerHost, "connections-per-host", 1, "Number of times to connect to each host (results in more output)") flag.BoolVar(&config.Banners, "banners", false, "Read banner upon connection creation") flag.StringVar(&messageFileName, "data", "", "Send a message and read response (%s will be replaced with destination IP)") flag.StringVar(&config.HTTP.Endpoint, "http", "", "Send an HTTP request to an endpoint") flag.StringVar(&config.HTTP.Method, "http-method", "GET", "Set HTTP request method type") flag.StringVar(&config.HTTP.UserAgent, "http-user-agent", "", "Set a custom HTTP user agent") flag.StringVar(&config.HTTP.ProxyDomain, "http-proxy-domain", "", "Send a CONNECT <domain> first") flag.IntVar(&config.HTTP.MaxSize, "http-max-size", 256, "Max kilobytes to read in response to an HTTP request") flag.BoolVar(&config.TLSExtendedRandom, "tls-extended-random", false, "send extended random extension") flag.StringVar(&config.EHLODomain, "ehlo", "", "Send an EHLO with the specified domain (implies --smtp)") flag.BoolVar(&config.SMTPHelp, "smtp-help", false, "Send a SMTP help (implies --smtp)") flag.BoolVar(&config.StartTLS, "starttls", false, "Send STARTTLS before negotiating") flag.BoolVar(&config.SMTP, "smtp", false, "Conform to SMTP when reading responses and sending STARTTLS") flag.BoolVar(&config.IMAP, "imap", false, "Conform to IMAP rules when sending STARTTLS") flag.BoolVar(&config.POP3, "pop3", false, "Conform to POP3 rules when sending STARTTLS") flag.BoolVar(&config.Modbus, "modbus", false, "Send some modbus data") flag.BoolVar(&config.S7, "s7", false, "send some s7 data") flag.BoolVar(&config.NoSNI, "no-sni", false, "Do not send domain name in TLS handshake regardless of whether known") flag.BoolVar(&config.ExportsOnly, "export-ciphers", false, "Send only export ciphers") flag.BoolVar(&config.ExportsDHOnly, "export-dhe-ciphers", false, "Send only export DHE ciphers") flag.BoolVar(&config.DHEOnly, "dhe-ciphers", false, "Send only DHE ciphers (not ECDHE)") flag.BoolVar(&config.ChromeOnly, "chrome-ciphers", false, "Send Chrome Ordered Cipher Suites") flag.BoolVar(&config.ChromeNoDHE, "chrome-no-dhe-ciphers", false, "Send chrome ciphers minus DHE suites") flag.BoolVar(&config.FirefoxOnly, "firefox-ciphers", false, "Send Firefox Ordered Cipher Suites") flag.BoolVar(&config.SafariOnly, "safari-ciphers", false, "Send Safari Ordered Cipher Suites") flag.BoolVar(&config.SafariNoDHE, "safari-no-dhe-ciphers", false, "Send Safari ciphers minus DHE suites") flag.BoolVar(&config.Heartbleed, "heartbleed", false, "Check if server is vulnerable to Heartbleed (implies --tls)") flag.StringVar(&rootCAFileName, "ca-file", "", "List of trusted root certificate authorities in PEM format") flag.IntVar(&config.GOMAXPROCS, "gomaxprocs", 3, "Set GOMAXPROCS (default 3)") flag.BoolVar(&config.FTP, "ftp", false, "Read FTP banners") flag.BoolVar(&config.SSH.SSH, "ssh", false, "SSH scan") flag.StringVar(&config.SSH.Client, "ssh-client", "", "Mimic behavior of a specific SSH client") flag.StringVar(&config.SSH.KexAlgorithms, "ssh-kex-algorithms", "", "Set SSH Key Exchange Algorithms") flag.StringVar(&config.SSH.HostKeyAlgorithms, "ssh-host-key-algorithms", "", "Set SSH Host Key Algorithms") flag.Parse() // Validate Go Runtime config if config.GOMAXPROCS < 1 { zlog.Fatalf("Invalid GOMAXPROCS (must be at least 1, given %d)", config.GOMAXPROCS) } // Stop the lowliest idiot from using this to DoS people if config.ConnectionsPerHost > 50 || config.ConnectionsPerHost < 1 { zlog.Fatalf("--connections-per-host must be in the range [0,50]") } // Validate SSH related flags if config.SSH.SSH { if _, ok := config.SSH.GetClientImplementation(); !ok { zlog.Fatalf("Unknown SSH client %s", config.SSH.Client) } if _, err := config.SSH.MakeKexNameList(); err != nil { zlog.Fatalf("Bad SSH Key Exchange Algorithms: %s", err.Error()) } if _, err := config.SSH.MakeHostKeyNameList(); err != nil { zlog.Fatalf("Bad SSH Host Key Algorithms: %s", err.Error()) } } // Validate HTTP if config.HTTP.Method != "GET" { zlog.Fatalf("Bad HTTP Method: %s (should be GET or just GET, really)", config.HTTP.Method) } // Validate FTP if config.FTP && config.Banners { zlog.Fatal("--ftp and --banners are mutually exclusive") } // Validate TLS Versions tv := strings.ToUpper(tlsVersion) if tv != "" { config.TLS = true } if config.TLS { switch tv { case "SSLV3", "SSLV30", "SSLV3.0": config.TLSVersion = ztls.VersionSSL30 tlsVersion = "SSLv3" case "TLSV1", "TLSV10", "TLSV1.0": config.TLSVersion = ztls.VersionTLS10 tlsVersion = "TLSv1.0" case "TLSV11", "TLSV1.1": config.TLSVersion = ztls.VersionTLS11 tlsVersion = "TLSv1.1" case "", "TLSV12", "TLSV1.2": config.TLSVersion = ztls.VersionTLS12 tlsVersion = "TLSv1.2" default: zlog.Fatal("Invalid SSL/TLS versions") } } // STARTTLS cannot be used with TLS if config.StartTLS && config.TLS { zlog.Fatal("Cannot both initiate a TLS and STARTTLS connection") } if config.EHLODomain != "" { config.EHLO = true } if config.SMTPHelp || config.EHLO { config.SMTP = true } if config.SMTP && (config.IMAP || config.POP3) { zlog.Fatal("Cannot conform to SMTP and IMAP/POP3 at the same time") } if config.IMAP && config.POP3 { zlog.Fatal("Cannot conform to IMAP and POP3 at the same time") } if config.EHLO && (config.IMAP || config.POP3) { zlog.Fatal("Cannot send an EHLO when conforming to IMAP or POP3") } if config.SMTP { mailType = "SMTP" } else if config.POP3 { mailType = "POP3" } else if config.IMAP { mailType = "IMAP" } // Heartbleed requires STARTTLS or TLS if config.Heartbleed && !(config.StartTLS || config.TLS) { zlog.Fatal("Must specify one of --tls or --starttls for --heartbleed") } encoding = strings.ToLower(config.Encoding) // Check output encoding switch encoding { case "string": case "base64": case "hex": default: zlog.Fatalf("Invalid encoding '%s'", config.Encoding) } config.Encoding = encoding // Validate port if portFlag > 65535 { zlog.Fatal("Port", portFlag, "out of range") } config.Port = uint16(portFlag) // Validate timeout config.Timeout = time.Duration(timeout) * time.Second // Validate senders if config.Senders == 0 { zlog.Fatal("Error: Need at least one sender") } // Check the network interface var err error // Look at CA file if rootCAFileName != "" { var fd *os.File if fd, err = os.Open(rootCAFileName); err != nil { zlog.Fatal(err) } caBytes, readErr := ioutil.ReadAll(fd) if readErr != nil { zlog.Fatal(err) } config.RootCAPool = x509.NewCertPool() ok := config.RootCAPool.AppendCertsFromPEM(caBytes) if !ok { zlog.Fatal("Could not read certificates from PEM file. Invalid PEM?") } } // Open input and output files switch inputFileName { case "-": inputFile = os.Stdin default: if inputFile, err = os.Open(inputFileName); err != nil { zlog.Fatal(err) } } switch outputFileName { case "-": outputConfig.OutputFile = os.Stdout default: if outputConfig.OutputFile, err = os.Create(outputFileName); err != nil { zlog.Fatal(err) } } // Open message file, if applicable if messageFileName != "" { if messageFile, err := os.Open(messageFileName); err != nil { zlog.Fatal(err) } else { buf := make([]byte, 1024) n, err := messageFile.Read(buf) config.SendData = true config.Data = buf[0:n] if err != nil && err != io.EOF { zlog.Fatal(err) } messageFile.Close() } } // Open metadata file if metadataFileName == "-" { metadataFile = os.Stdout } else { if metadataFile, err = os.Create(metadataFileName); err != nil { zlog.Fatal(err) } } // Open log file, attach to configs var logFile *os.File if logFileName == "-" { logFile = os.Stderr } else { if logFile, err = os.Create(logFileName); err != nil { zlog.Fatal(err) } } logger := zlog.New(logFile, "banner-grab") config.ErrorLog = logger }
// processCertsFromClient takes a chain of client certificates either from a // Certificates message or from a sessionState and verifies them. It returns // the public key of the leaf certificate. func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (crypto.PublicKey, error) { c := hs.c hs.certsFromClient = certificates certs := make([]*x509.Certificate, len(certificates)) var err error for i, asn1Data := range certificates { if certs[i], err = x509.ParseCertificate(asn1Data); err != nil { c.sendAlert(alertBadCertificate) return nil, errors.New("tls: failed to parse client certificate: " + err.Error()) } } if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 { opts := x509.VerifyOptions{ Roots: c.config.ClientCAs, CurrentTime: c.config.time(), Intermediates: x509.NewCertPool(), KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, } for _, cert := range certs[1:] { opts.Intermediates.AddCert(cert) } chains, err := certs[0].Verify(opts) if err != nil { c.sendAlert(alertBadCertificate) return nil, errors.New("tls: failed to verify client's certificate: " + err.Error()) } ok := false for _, ku := range certs[0].ExtKeyUsage { if ku == x509.ExtKeyUsageClientAuth { ok = true break } } if !ok { c.sendAlert(alertHandshakeFailure) return nil, errors.New("tls: client's certificate's extended key usage doesn't permit it to be used for client authentication") } c.verifiedChains = chains } if len(certs) > 0 { var pub crypto.PublicKey switch key := certs[0].PublicKey.(type) { case *ecdsa.PublicKey, *rsa.PublicKey: pub = key default: c.sendAlert(alertUnsupportedCertificate) return nil, fmt.Errorf("tls: client's certificate contains an unsupported public key of type %T", certs[0].PublicKey) } c.peerCertificates = certs return pub, nil } return nil, nil }