package pkg import ( "errors" "strconv" "strings" ) type ( KeyChain struct { AppService string `json:"app_service"` Cert string `json:"cert"` Chain string `json:"chain"` Key string `json:"key"` OCSPStaplingParams string `json:"ocsp_stapling_params"` Passphrase string `json:"passphrase"` } ClientSSLProfile struct { ProfileCommon AlertTimeout string `json:"alert_timeout"` AllowDynamicRecordSizing string `json:"allow_dynamic_record_sizing"` AllowExpiredCRL string `json:"allow_expired_crl"` AllowNonSSL string `json:"allow_non_ssl"` Authenticate string `json:"authenticate"` AuthenticateDepth int `json:"authenticate_depth"` // authenticate_depth 9 CAFile string `json:"ca_file"` // ca_file none CacheSize int `json:"cache_size"` // cache_size 262144 CacheTimeout int `json:"cache_timeout"` // cache_timeout 3600 Cert string `json:"cert"` // cert default.crt CertExtensionIncludes []string `json:"cert_extension_includes"` CertKeyChain map[string]KeyChain `json:"cert_key_chain"` CertLifespan int `json:"cert_lifespan"` CertLookupByIPAddrPort string `json:"cert_lookup_by_ipaddr_port"` Chain string `json:"chain"` Ciphers string `json:"ciphers"` ClientCertCA string `json:"client_cert_ca"` CRLFile string `json:"crl_file"` DestinationIPBlacklist string `json:"destination_ip_blacklist"` DestinationIPWhitelist string `json:"destination_ip_whitelist"` ForwardProxyBypassDefaultAction string `json:"forward_proxy_bypass_default_action"` GenericAlert string `json:"generic_alert"` HandshakeTimeout int `json:"handshake_timeout"` HostnameBlacklist string `json:"hostname_blacklist"` HostnameWhitelist string `json:"hostname_whitelist"` InheritCertKeyChain string `json:"inherit_certkeychain"` Key string `json:"key"` MaxActiveHandshakes string `json:"max_active_handshakes"` MaxAggregateRenegotiationPerMinute string `json:"max_aggregate_renegotiation_per_minute"` MaxRenegotiationsPerMinute int `json:"max_renegotiations_per_minute"` MaximumRecordSize int `json:"maximum_record_size"` ModSSLMethods string `json:"mod_ssl_methods"` Mode string `json:"mode"` Options []string `json:"options"` Passphrase string `json:"passphrase"` PeerCertMode string `json:"peer_cert_mode"` PeerNoRenegotiateTimeout int `json:"peer_no_renegotiate_timeout"` ProxyCACert string `json:"proxy_ca_cert"` ProxyCAKey string `json:"proxy_ca_key"` PRoxyCAPassphrase string `json:"proxy_ca_passphrase"` ProxySSL string `json:"proxy_ssl"` ProxySSLPassthrough string `json:"proxy_ssl_passthrough"` RenegotiateMaxRecordDelay string `json:"renegotiate_max_record_delay"` RenegotiatePeriod string `json:"renegotiate_period"` RenegotiateSize string `json:"renegotiate_size"` Renegotiation string `json:"renegotiation"` RetainCertificate string `json:"retain_certificate"` SecureRenegotiation string `json:"secure_renegotiation"` ServerName string `json:"server_name"` SessionMirroring string `json:"session_mirroring"` SessionTicket string `json:"session_ticket"` SessionTicketTimeout int `json:"session_ticket_timeout"` SNIDefault string `json:"sni_default"` SNIRequire string `json:"sni_require"` SourceIPBlacklist string `json:"source_ip_blacklist"` SourceIPWhitelist string `json:"source_ip_whitelist"` SSLForwardProxy string `json:"ssl_forward_proxy"` SSLForwardProxyBypass string `json:"ssl_forward_proxy_bypass"` SSLSignHash string `json:"ssl_sign_hash"` StrictResume string `json:"strict_resume"` UncleanShutdown string `json:"unclean_shutdown"` } ) func ParseKeyChain(raw string) (KeyChain, string) { var k KeyChain var n string for i, l := range strings.Split(raw, "\n") { f := strings.Fields(l) // split the line in to fields if len(f) == 0 { continue // skip blank lines } if i == 0 { n = f[0] // grab the name for later use continue } switch strings.ToLower(f[0]) { case "app-service": k.AppService = strings.Join(f[1:], " ") case "cert": k.Cert = strings.Join(f[1:], " ") case "chain": k.Chain = strings.Join(f[1:], " ") case "key": k.Key = strings.Join(f[1:], " ") case "ocsp-stapling-params": k.OCSPStaplingParams = strings.Join(f[1:], " ") case "passphrase": k.Passphrase = strings.Join(f[1:], " ") } } return k, n } func ParseClientSSLProfile(raw string) (*ClientSSLProfile, error) { p := new(ClientSSLProfile) split := strings.Split(raw, "\n") if !strings.Contains(split[0], "ltm profile client-ssl") { return nil, errors.New("not a client-ssl profile") } p.ProfileCommon = parseCommon(raw) // initialize some placeholders p.CertKeyChain = make(map[string]KeyChain) p.Options = []string{} inKeyChains := false var keyChain string for _, line := range split { if inKeyChains { if line == " }" { // we've copied the whole keychain to our holder, parse it k, n := ParseKeyChain(keyChain) p.CertKeyChain[n] = k inKeyChains = false keyChain = "" // reset the keychain holder continue } // build a copy of this particular keychain in our holder keyChain = keyChain + line + "\n" continue } if line == " cert-key-chain {" { inKeyChains = true continue } f := strings.Fields(line) switch strings.ToLower(f[0]) { case "alert-timeout": p.AlertTimeout = strings.Join(f[1:], " ") case "allow-dynamic-record-sizing": p.AllowDynamicRecordSizing = strings.Join(f[1:], " ") case "allow-expired-crl": p.AllowExpiredCRL = strings.Join(f[1:], " ") case "allow-non-ssl": p.AllowNonSSL = strings.Join(f[1:], " ") case "authenticate": p.Authenticate = strings.Join(f[1:], " ") case "authenticate-depth": i, err := strconv.Atoi(f[1]) if err != nil { log.WithField("data", f[1]).Error(err.Error()) return nil, err } p.AuthenticateDepth = i case "ca-file": p.CAFile = strings.Join(f[1:], " ") case "cache-size": i, err := strconv.Atoi(f[1]) if err != nil { log.WithField("data", f[1]).Error(err.Error()) return nil, err } p.CacheSize = i case "cache-timeout": i, err := strconv.Atoi(f[1]) if err != nil { log.WithField("data", f[1]).Error(err.Error()) return nil, err } p.CacheTimeout = i case "cert": p.Cert = strings.Join(f[1:], " ") case "cert-extension-includes": for i, n := range f { if i == 0 || n == "{" || n == "}" { continue } p.CertExtensionIncludes = append(p.CertExtensionIncludes, n) } case "cert-lifespan": i, err := strconv.Atoi(f[1]) if err != nil { log.WithField("data", f[1]).Error(err.Error()) return nil, err } p.CertLifespan = i case "cert-lookup-by-ipaddr-port": p.CertLookupByIPAddrPort = strings.Join(f[1:], " ") case "chain": p.Chain = strings.Join(f[1:], " ") case "ciphers": p.Ciphers = strings.Join(f[1:], " ") case "client-cert-ca": p.ClientCertCA = strings.Join(f[1:], " ") case "crl-file": p.CRLFile = strings.Join(f[1:], " ") case "destination-ip-blacklist": p.DestinationIPBlacklist = strings.Join(f[1:], " ") case "destination-ip-whitelist": p.DestinationIPWhitelist = strings.Join(f[1:], " ") case "forward-proxy-bypass-default-action": p.ForwardProxyBypassDefaultAction = strings.Join(f[1:], " ") case "generic-alert": p.GenericAlert = strings.Join(f[1:], " ") case "handshake-timeout": i, err := strconv.Atoi(f[1]) if err != nil { log.WithField("data", f[1]).Error(err.Error()) return nil, err } p.HandshakeTimeout = i case "hostname-blacklist": p.HostnameBlacklist = strings.Join(f[1:], " ") case "hostname-whitelist": p.HostnameWhitelist = strings.Join(f[1:], " ") case "inherit-certkeychain": p.InheritCertKeyChain = strings.Join(f[1:], " ") case "key": p.Key = strings.Join(f[1:], " ") case "max-active-handshakes": p.MaxActiveHandshakes = strings.Join(f[1:], " ") case "max-aggregate-renegotiation-per-minute": p.MaxAggregateRenegotiationPerMinute = strings.Join(f[1:], " ") case "max-renegotiations-per-minute": i, err := strconv.Atoi(f[1]) if err != nil { log.WithField("data", f[1]).Error(err.Error()) return nil, err } p.MaxRenegotiationsPerMinute = i case "maximum-record-size": i, err := strconv.Atoi(f[1]) if err != nil { log.WithField("data", f[1]).Error(err.Error()) return nil, err } p.MaximumRecordSize = i case "mod-ssl-methods": p.ModSSLMethods = strings.Join(f[1:], " ") case "mode": p.Mode = strings.Join(f[1:], " ") case "options": if f[1] != "none" { p.Options = append(p.Options, f[2:len(f)-1]...) } case "passphrase": p.Passphrase = strings.Join(f[1:], " ") case "peer-cert-mode": p.PeerCertMode = strings.Join(f[1:], " ") case "peer-no-renegotiate-timeout": i, err := strconv.Atoi(f[1]) if err != nil { log.WithField("data", f[1]).Error(err.Error()) return nil, err } p.PeerNoRenegotiateTimeout = i case "proxy-ca-cert": p.ProxyCACert = strings.Join(f[1:], " ") case "proxy-ca-key": p.ProxyCAKey = strings.Join(f[1:], " ") case "proxy-ca-passphrase": p.PRoxyCAPassphrase = strings.Join(f[1:], " ") case "proxy-ssl": p.ProxySSL = strings.Join(f[1:], " ") case "proxy-ssl-passthrough": p.ProxySSLPassthrough = strings.Join(f[1:], " ") case "renegotiate-max-record-delay": p.RenegotiateMaxRecordDelay = strings.Join(f[1:], " ") case "renegotiate-period": p.RenegotiatePeriod = strings.Join(f[1:], " ") case "renegotiate-size": p.RenegotiateSize = strings.Join(f[1:], " ") case "renegotiation": p.Renegotiation = strings.Join(f[1:], " ") case "retain-certificate": p.RetainCertificate = strings.Join(f[1:], " ") case "secure-renegotiation": p.SecureRenegotiation = strings.Join(f[1:], " ") case "server-name": p.ServerName = strings.Join(f[1:], " ") case "session-mirroring": p.SessionMirroring = strings.Join(f[1:], " ") case "session-ticket": p.SessionTicket = strings.Join(f[1:], " ") case "session-ticket-timeout": i, err := strconv.Atoi(f[1]) if err != nil { log.WithField("data", f[1]).Error(err.Error()) return nil, err } p.SessionTicketTimeout = i case "sni-default": p.SNIDefault = strings.Join(f[1:], " ") case "sni-require": p.SNIRequire = strings.Join(f[1:], " ") case "source-ip-blacklist": p.SourceIPBlacklist = strings.Join(f[1:], " ") case "source-ip-whitelist": p.SourceIPWhitelist = strings.Join(f[1:], " ") case "ssl-forward-proxy": p.SSLForwardProxy = strings.Join(f[1:], " ") case "ssl-forward-proxy-bypass": p.SSLForwardProxyBypass = strings.Join(f[1:], " ") case "ssl-sign-hash": p.SSLSignHash = strings.Join(f[1:], " ") case "strict-resume": p.StrictResume = strings.Join(f[1:], " ") case "unclean-shutdown": p.UncleanShutdown = strings.Join(f[1:], " ") } } return p, nil }