From 7d80a9b913eaf966b8c0a52bad4a8db5ecd89075 Mon Sep 17 00:00:00 2001 From: Durand Fabrice Date: Fri, 7 Mar 2025 11:27:14 -0500 Subject: [PATCH] Search for the shared secret of the switch --- go/chisel/server/server_handler.go | 2 + go/chisel/server/server_listen.go | 31 +++--- go/chisel/share/radius_proxy/proxy.go | 78 ++++++++++++- go/pfconfigdriver/structs.go | 151 +++++++++++++++++++++----- 4 files changed, 214 insertions(+), 48 deletions(-) diff --git a/go/chisel/server/server_handler.go b/go/chisel/server/server_handler.go index a3844566bb3e..b984f081bd3b 100644 --- a/go/chisel/server/server_handler.go +++ b/go/chisel/server/server_handler.go @@ -178,6 +178,8 @@ func (s *Server) handleWebsocket(w http.ResponseWriter, req *http.Request) { localSecret := pfconfigdriver.LocalSecret{} pfconfigdriver.FetchDecodeSocket(req.Context(), &localSecret) + + // //successfuly validated config! r.Reply(true, nil) //tunnel per ssh connection diff --git a/go/chisel/server/server_listen.go b/go/chisel/server/server_listen.go index acd57622c7c2..11b106e46c77 100644 --- a/go/chisel/server/server_listen.go +++ b/go/chisel/server/server_listen.go @@ -5,7 +5,6 @@ import ( "crypto/x509" "errors" "fmt" - "io/ioutil" "net" "os" "os/user" @@ -15,7 +14,7 @@ import ( "golang.org/x/crypto/acme/autocert" ) -//TLSConfig enables configures TLS +// TLSConfig enables configures TLS type TLSConfig struct { Key string Cert string @@ -44,12 +43,12 @@ func (s *Server) listener(host, port string) (net.Listener, error) { extra = " (WARNING: LetsEncrypt will attempt to connect to your domain on port 443)" } } - //tcp listen + // tcp listen l, err := net.Listen("tcp", host+":"+port) if err != nil { return nil, err } - //optionally wrap in tls + // optionally wrap in tls proto := "http" if tlsConf != nil { proto += "s" @@ -64,7 +63,7 @@ func (s *Server) listener(host, port string) (net.Listener, error) { } func (s *Server) tlsLetsEncrypt(domains []string) *tls.Config { - //prepare cert manager + // prepare cert manager m := &autocert.Manager{ Prompt: func(tosURL string) bool { s.Infof("Accepting LetsEncrypt TOS and fetching certificate...") @@ -73,7 +72,7 @@ func (s *Server) tlsLetsEncrypt(domains []string) *tls.Config { Email: settings.Env("LE_EMAIL"), HostPolicy: autocert.HostWhitelist(domains...), } - //configure file cache + // configure file cache c := settings.Env("LE_CACHE") if c == "" { h := os.Getenv("HOME") @@ -88,7 +87,7 @@ func (s *Server) tlsLetsEncrypt(domains []string) *tls.Config { s.Infof("LetsEncrypt cache directory %s", c) m.Cache = autocert.DirCache(c) } - //return lets-encrypt tls config + // return lets-encrypt tls config return m.TLSConfig() } @@ -97,11 +96,11 @@ func (s *Server) tlsKeyCert(key, cert string, ca string) (*tls.Config, error) { if err != nil { return nil, err } - //file based tls config using tls defaults + // file based tls config using tls defaults c := &tls.Config{ Certificates: []tls.Certificate{keypair}, } - //mTLS requires server's CA + // mTLS requires server's CA if ca != "" { if err := addCA(ca, c); err != nil { return nil, err @@ -118,12 +117,12 @@ func addCA(ca string, c *tls.Config) error { } clientCAPool := x509.NewCertPool() if fileInfo.IsDir() { - //this is a directory holding CA bundle files - files, err := ioutil.ReadDir(ca) + // this is a directory holding CA bundle files + files, err := os.ReadDir(ca) if err != nil { return err } - //add all cert files from path + // add all cert files from path for _, file := range files { f := file.Name() if err := addPEMFile(filepath.Join(ca, f), clientCAPool); err != nil { @@ -131,24 +130,24 @@ func addCA(ca string, c *tls.Config) error { } } } else { - //this is a CA bundle file + // this is a CA bundle file if err := addPEMFile(ca, clientCAPool); err != nil { return err } } - //set client CAs and enable cert verification + // set client CAs and enable cert verification c.ClientCAs = clientCAPool c.ClientAuth = tls.RequireAndVerifyClientCert return nil } func addPEMFile(path string, pool *x509.CertPool) error { - content, err := ioutil.ReadFile(path) + content, err := os.ReadFile(path) if err != nil { return err } if !pool.AppendCertsFromPEM(content) { - return errors.New("Fail to load certificates from : " + path) + return fmt.Errorf("fail to load certificates from : %s", path) } return nil } diff --git a/go/chisel/share/radius_proxy/proxy.go b/go/chisel/share/radius_proxy/proxy.go index da5171da3707..bd93b0b26b15 100644 --- a/go/chisel/share/radius_proxy/proxy.go +++ b/go/chisel/share/radius_proxy/proxy.go @@ -1,13 +1,17 @@ package radius_proxy import ( + "context" "crypto/hmac" "crypto/md5" "errors" + "net" + "strings" "time" "github.com/google/uuid" "github.com/inverse-inc/packetfence/go/chisel/share/cio" + "github.com/inverse-inc/packetfence/go/pfconfigdriver" "layeh.com/radius" "layeh.com/radius/rfc2865" "layeh.com/radius/rfc2869" @@ -95,10 +99,17 @@ func (rp *Proxy) ProxyPacket(payload []byte, connectorID string) ([]byte, string } packet.Attributes.Add(26, vsa) - // err = addMessageAuthenticator(packet, []byte(rp.secret)) - // if err != nil { - // return nil, "", err - // } + + secret, err := rp.foundSecret(context.Background(), packet) + + if err != nil { + secret = string(rp.secret) + } + + err = addMessageAuthenticator(packet, []byte(secret)) + if err != nil { + return nil, "", err + } b2, err := packet.Encode() if err != nil { @@ -131,3 +142,62 @@ func addMessageAuthenticator(p *radius.Packet, secret []byte) error { rfc2869.MessageAuthenticator_Set(p, hash.Sum(nil)) return nil } + +func (rp *Proxy) foundSecret(ctx context.Context, packet *radius.Packet) (string, error) { + + var SwitchID []string + SwitchMAC := rfc2865.CallingStationID_GetString(packet) + if SwitchMAC != "" { + MacHW, err := net.ParseMAC(SwitchMAC) + if err == nil { + SwitchMAC = MacHW.String() + SwitchID = append(SwitchID, SwitchMAC) + } + } + + SwitchNasIP := rfc2865.NASIPAddress_Get(packet) + if SwitchNasIP != nil { + SwitchID = append(SwitchID, SwitchNasIP.String()) + } + + switches := pfconfigdriver.PfSwitches{} + pfconfigdriver.FetchDecodeSocket(ctx, &switches) + + for _, switchID := range SwitchID { + + // Find the switch with the given ID + for _, sw := range switches.PfconfigKeys.Keys { + if sw != switchID { + continue + } + switche := pfconfigdriver.PfConfSwitch{} + switche.PfconfigHashNS = sw + pfconfigdriver.FetchDecodeSocket(ctx, &switche) + if switche.RadiusSecret.String() != "" { + return switche.RadiusSecret.String(), nil + } + } + // Find the switch within the ip ranges + if IsIPv4(net.ParseIP(switchID)) { + for _, sw := range switches.PfconfigKeys.Keys { + _, network, err := net.ParseCIDR(sw) + if err != nil { + continue + } + if network.Contains(net.ParseIP(switchID)) { + switche := pfconfigdriver.PfConfSwitch{} + switche.PfconfigHashNS = sw + pfconfigdriver.FetchDecodeSocket(ctx, &switche) + if switche.RadiusSecret.String() != "" { + return switche.RadiusSecret.String(), nil + } + } + } + } + } + return "", errors.New("No secret found") +} + +func IsIPv4(address net.IP) bool { + return strings.Count(address.String(), ":") < 2 +} diff --git a/go/pfconfigdriver/structs.go b/go/pfconfigdriver/structs.go index 809a086ae177..fd7f167f9541 100644 --- a/go/pfconfigdriver/structs.go +++ b/go/pfconfigdriver/structs.go @@ -579,34 +579,34 @@ type ClusterSummary struct { type PfConfAdvanced struct { StructConfig - PfconfigMethod string `val:"hash_element"` - PfconfigNS string `val:"config::Pf"` - PfconfigHashNS string `val:"advanced"` - HashingCost string `json:"hashing_cost"` - ScanOnAccounting string `json:"scan_on_accounting"` - PffilterProcesses string `json:"pffilter_processes"` - UpdateIplogWithAccounting string `json:"update_iplog_with_accounting"` - UpdateIplogWithAuthentication string `json:"update_iplog_with_authentication"` - AdminCspSecurityHeaders string `json:"admin_csp_security_headers"` - Multihost string `json:"multihost"` - SsoOnAccessReevaluation string `json:"sso_on_access_reevaluation"` - DisablePfDomainAuth string `json:"disable_pf_domain_auth"` - TimingStatsLevel string `json:"timing_stats_level"` - SsoOnDhcp string `json:"sso_on_dhcp"` - Language string `json:"language"` - StatsdListenPort string `json:"statsd_listen_port"` - SsoOnAccounting string `json:"sso_on_accounting"` - LocationlogCloseOnAccountingStop string `json:"locationlog_close_on_accounting_stop"` - PortalCspSecurityHeaders string `json:"portal_csp_security_headers"` - HashPasswords string `json:"hash_passwords"` - SourceToSendSmsWhenCreatingUsers string `json:"source_to_send_sms_when_creating_users"` - ActiveDirectoryOsJoinCheckBypass string `json:"active_directory_os_join_check_bypass"` - PfperlApiTimeout string `json:"pfperl_api_timeout"` - LdapAttributes []string `json:"ldap_attributes"` - ApiInactivityTimeout int `json:"api_inactivity_timeout"` - ApiMaxExpiration int `json:"api_max_expiration"` - NetFlowOnAllNetworks string `json:"netflow_on_all_networks"` - AccountingTimebucketSize int `json:"accounting_timebucket_size"` + PfconfigMethod string `val:"hash_element"` + PfconfigNS string `val:"config::Pf"` + PfconfigHashNS string `val:"advanced"` + HashingCost string `json:"hashing_cost"` + ScanOnAccounting string `json:"scan_on_accounting"` + PffilterProcesses string `json:"pffilter_processes"` + UpdateIplogWithAccounting string `json:"update_iplog_with_accounting"` + UpdateIplogWithAuthentication string `json:"update_iplog_with_authentication"` + AdminCspSecurityHeaders string `json:"admin_csp_security_headers"` + Multihost string `json:"multihost"` + SsoOnAccessReevaluation string `json:"sso_on_access_reevaluation"` + DisablePfDomainAuth string `json:"disable_pf_domain_auth"` + TimingStatsLevel string `json:"timing_stats_level"` + SsoOnDhcp string `json:"sso_on_dhcp"` + Language string `json:"language"` + StatsdListenPort string `json:"statsd_listen_port"` + SsoOnAccounting string `json:"sso_on_accounting"` + LocationlogCloseOnAccountingStop string `json:"locationlog_close_on_accounting_stop"` + PortalCspSecurityHeaders string `json:"portal_csp_security_headers"` + HashPasswords string `json:"hash_passwords"` + SourceToSendSmsWhenCreatingUsers string `json:"source_to_send_sms_when_creating_users"` + ActiveDirectoryOsJoinCheckBypass string `json:"active_directory_os_join_check_bypass"` + PfperlApiTimeout string `json:"pfperl_api_timeout"` + LdapAttributes []string `json:"ldap_attributes"` + ApiInactivityTimeout int `json:"api_inactivity_timeout"` + ApiMaxExpiration int `json:"api_max_expiration"` + NetFlowOnAllNetworks string `json:"netflow_on_all_networks"` + AccountingTimebucketSize int `json:"accounting_timebucket_size"` } type PfConfDns struct { @@ -967,3 +967,98 @@ type FingerbankSettings struct { } var FingerbankConf = FingerbankSettings{} + +type PfConfSwitch struct { + StructConfig + PfconfigMethod string `val:"hash_element"` + PfconfigNS string `val:"config::Switch"` + PfconfigHashNS string `val:"-"` // Will be the switch IP + + // Basic Information + Type string `json:"type"` + Description string `json:"description"` + Group string `json:"group"` + + // VLANs + Vlans map[string]string `json:"vlans"` + VlanMap string `json:"VlanMap"` + + // Interfaces + Interfaces map[string]interface{} `json:"interfaces"` + + //Roles + Roles map[string]string `json:"roles"` + RoleMap string `json:"RoleMap"` + + // Network Related + Networks map[string]interface{} `json:"networks"` + NetworksFrom map[string]interface{} `json:"networks_from"` + + // Access Control + AccessLists map[string]interface{} `json:"access_lists"` + AccessListMap string `json:"AccessListMap"` + ACLsLimit string `json:"ACLsLimit"` + DownloadableACLsLimit string `json:"DownloadableACLsLimit"` + UsePushACLs string `json:"UsePushACLs"` + UseDownloadableACLs string `json:"UseDownloadableACLs"` + PushACLs string `json:"pushACLs"` + + // WebAuth + UrlMap string `json:"UrlMap"` + Urls map[string]interface{} `json:"urls"` + ExternalPortalEnforcement string `json:"ExternalPortalEnforcement"` + + // VPN + Vpn map[string]interface{} `json:"vpn"` + VpnMap string `json:"VpnMap"` + + // MFA + PostMfaValidation string `json:"PostMfaValidation"` + + // SNMP + SNMPUseConnector string `json:"SNMPUseConnector"` + SNMPVersion string `json:"SNMPVersion"` + SNMPVersionTrap string `json:"SNMPVersionTrap"` + SNMPCommunityRead string `json:"SNMPCommunityRead"` + SNMPCommunityWrite string `json:"SNMPCommunityWrite"` + SNMPCommunityTrap string `json:"SNMPCommunityTrap"` + + // CLI + CliAccess string `json:"cliAccess"` + CliUser string `json:"cliUser"` + CliPwd pfcrypt.CryptString `json:"cliPwd"` + CliEnablePwd pfcrypt.CryptString `json:"cliEnablePwd"` + CliTransport string `json:"cliTransport"` + + // Web Services + WsTransport string `json:"wsTransport"` + WsUser string `json:"wsUser"` + WsPwd pfcrypt.CryptString `json:"wsPwd"` + + // VoIP Detection + VoIPEnabled int `json:"VoIPEnabled"` + VoIPCDPDetect string `json:"VoIPCDPDetect"` + VoIPDHCPDetect string `json:"VoIPDHCPDetect"` + VoIPLLDPDetect string `json:"VoIPLLDPDetect"` + + // RADIUS + UseCoA string `json:"useCoA"` + RadiusSecret pfcrypt.CryptString `json:"radiusSecret"` + RadiusDeauthUseConnector string `json:"radiusDeauthUseConnector"` + + //Advanced + InlineTrigger []interface{} `json:"inlineTrigger"` + Mode string `json:"mode"` + MacSearchesMaxNb string `json:"macSearchesMaxNb"` + MacSearchesSleepInterval string `json:"macSearchesSleepInterval"` + DeauthOnPrevious string `json:"deauthOnPrevious"` + Uplink []string `json:"uplink"` +} + +// PfSwitch struct that will help when fetching multiple switch config from config::Switch +type PfSwitches struct { + PfconfigKeys + PfconfigMethod string `val:"keys"` + PfconfigNS string `val:"config::Switch"` + Keys []string +}