// Copyright (c) quickfixengine.org All rights reserved. // // This file may be distributed under the terms of the quickfixengine.org // license as defined by quickfixengine.org and appearing in the file // LICENSE included in the packaging of this file. // // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING // THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE. // // See http://www.quickfixengine.org/LICENSE for licensing information. // // Contact ask@quickfixengine.org if any conditions of this licensing // are not clear to you. package quickfix import ( "crypto/tls" "crypto/x509" "errors" "fmt" "os" "quantex.com/qfixpt/quickfix/config" ) func loadTLSConfig(settings *SessionSettings) (*tls.Config, error) { var err error allowSkipClientCerts := false if settings.HasSetting(config.SocketUseSSL) { allowSkipClientCerts, err = settings.BoolSetting(config.SocketUseSSL) if err != nil { return nil, err } } var serverName string if settings.HasSetting(config.SocketServerName) { serverName, err = settings.Setting(config.SocketServerName) if err != nil { return nil, err } } insecureSkipVerify := false if settings.HasSetting(config.SocketInsecureSkipVerify) { insecureSkipVerify, err = settings.BoolSetting(config.SocketInsecureSkipVerify) if err != nil { return nil, err } } if !settings.HasSetting(config.SocketPrivateKeyFile) && !settings.HasSetting(config.SocketCertificateFile) && !settings.HasSetting(config.SocketPrivateKeyBytes) && !settings.HasSetting(config.SocketCertificateBytes) { if !allowSkipClientCerts { return nil, nil } } tlsConfig := defaultTLSConfig() tlsConfig.ServerName = serverName tlsConfig.InsecureSkipVerify = insecureSkipVerify setMinVersionExplicit(settings, tlsConfig) if settings.HasSetting(config.SocketPrivateKeyFile) || settings.HasSetting(config.SocketCertificateFile) { var privateKeyFile string var certificateFile string privateKeyFile, err = settings.Setting(config.SocketPrivateKeyFile) if err != nil { return nil, err } certificateFile, err = settings.Setting(config.SocketCertificateFile) if err != nil { return nil, err } tlsConfig.Certificates = make([]tls.Certificate, 1) if tlsConfig.Certificates[0], err = tls.LoadX509KeyPair(certificateFile, privateKeyFile); err != nil { return nil, fmt.Errorf("failed to load key pair: %w", err) } } else if settings.HasSetting(config.SocketPrivateKeyBytes) || settings.HasSetting(config.SocketCertificateBytes) { privateKeyBytes, err := settings.RawSetting(config.SocketPrivateKeyBytes) if err != nil { return nil, err } certificateBytes, err := settings.RawSetting(config.SocketCertificateBytes) if err != nil { return nil, err } tlsConfig.Certificates = make([]tls.Certificate, 1) certificate, err := tls.X509KeyPair(certificateBytes, privateKeyBytes) if err != nil { return nil, fmt.Errorf("failed to parse key pair: %w", err) } tlsConfig.Certificates[0] = certificate } if !allowSkipClientCerts { tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert } if !settings.HasSetting(config.SocketCAFile) && !settings.HasSetting(config.SocketCABytes) { return tlsConfig, nil } certPool := x509.NewCertPool() if settings.HasSetting(config.SocketCAFile) { caFile, err := settings.Setting(config.SocketCAFile) if err != nil { return nil, err } pem, err := os.ReadFile(caFile) if err != nil { return nil, fmt.Errorf("failed to read CA bundle: %w", err) } if !certPool.AppendCertsFromPEM(pem) { err = fmt.Errorf("failed to parse %v", caFile) return nil, err } } else { caBytes, err := settings.RawSetting(config.SocketCABytes) if err != nil { return nil, err } if !certPool.AppendCertsFromPEM(caBytes) { err = errors.New("failed to parse CA bundle from raw bytes") return nil, err } } tlsConfig.RootCAs = certPool tlsConfig.ClientCAs = certPool return tlsConfig, nil } // defaultTLSConfig brought to you by https://github.com/gtank/cryptopasta/ func defaultTLSConfig() *tls.Config { return &tls.Config{ // Avoids most of the memorably-named TLS attacks MinVersion: tls.VersionTLS12, // Causes servers to use Go's default ciphersuite preferences, // which are tuned to avoid attacks. Does nothing on clients. PreferServerCipherSuites: true, // Only use curves which have constant-time implementations CurvePreferences: []tls.CurveID{ tls.CurveP256, }, } } func setMinVersionExplicit(settings *SessionSettings, tlsConfig *tls.Config) { if settings.HasSetting(config.SocketMinimumTLSVersion) { minVersion, err := settings.Setting(config.SocketMinimumTLSVersion) if err != nil { return } switch minVersion { case "SSL30": //nolint:staticcheck // SA1019 min version ok tlsConfig.MinVersion = tls.VersionSSL30 case "TLS10": tlsConfig.MinVersion = tls.VersionTLS10 case "TLS11": tlsConfig.MinVersion = tls.VersionTLS11 case "TLS12": tlsConfig.MinVersion = tls.VersionTLS12 } } }