From 05217b67f2191c63783fec4a84bbd7d44e458022 Mon Sep 17 00:00:00 2001 From: greenstatic Date: Fri, 24 Aug 2018 09:26:27 +0200 Subject: [PATCH] added packet replay detection --- README.md | 1 - cmd/openspa-client/cmd/getip.go | 2 +- cmd/openspa-client/cmd/request.go | 8 ++-- cmd/openspa-client/cmd/root.go | 2 +- cmd/openspa-server/cmd/root.go | 2 +- cmd/openspa-server/cmd/server.go | 15 ++++--- cmd/openspa-tools/cmd/genClient.go | 2 +- cmd/openspa-tools/cmd/genServerKey.go | 2 +- internal/cmdimpl/getip.go | 2 +- internal/extensionScripts/ruleAdd.go | 2 +- internal/extensionScripts/ruleRemove.go | 2 +- internal/genOspa/generate.go | 4 +- internal/genOspa/walkthrough.go | 4 +- internal/ospa/ospa.go | 4 +- internal/server/receiving.go | 27 ++++++++++++- internal/server/replayDetection.go | 54 +++++++++++++++++++++++++ internal/server/server.go | 5 ++- internal/server/version.go | 2 +- 18 files changed, 110 insertions(+), 30 deletions(-) create mode 100644 internal/server/replayDetection.go diff --git a/README.md b/README.md index 251601f..223cb0e 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,6 @@ go get -u ./... ``` ## TODO -- [ ] Implement packet replay detection - [ ] Improve the firewalltracker package - [ ] OpenSPA Client support for encrypted private keys - [ ] OpenSPA Server public IP resolver diff --git a/cmd/openspa-client/cmd/getip.go b/cmd/openspa-client/cmd/getip.go index eaf27ca..21b993a 100644 --- a/cmd/openspa-client/cmd/getip.go +++ b/cmd/openspa-client/cmd/getip.go @@ -1,9 +1,9 @@ package cmd import ( - "github.com/spf13/cobra" "github.com/greenstatic/openspa/internal/cmdimpl" "github.com/greenstatic/openspa/internal/ipresolver" + "github.com/spf13/cobra" "os" ) diff --git a/cmd/openspa-client/cmd/request.go b/cmd/openspa-client/cmd/request.go index 7e76d9a..b8c5a75 100644 --- a/cmd/openspa-client/cmd/request.go +++ b/cmd/openspa-client/cmd/request.go @@ -2,6 +2,9 @@ package cmd import ( "crypto/rsa" + "github.com/greenstatic/openspa/internal/client" + "github.com/greenstatic/openspa/internal/ipresolver" + "github.com/greenstatic/openspa/internal/ospa" "github.com/greenstatic/openspalib/cryptography" "github.com/greenstatic/openspalib/response" "github.com/greenstatic/openspalib/tools" @@ -9,9 +12,6 @@ import ( "github.com/spf13/cobra" "io/ioutil" "net" - "github.com/greenstatic/openspa/internal/client" - "github.com/greenstatic/openspa/internal/ipresolver" - "github.com/greenstatic/openspa/internal/ospa" "os" "strconv" "time" @@ -399,7 +399,7 @@ func request(clientPrivKey *rsa.PrivateKey, clientPubKey *rsa.PublicKey, serverP } log.WithFields(log.Fields{ - "protocol": tools.ConvertProtoByteToStr(resp.Payload.Protocol), + "protocol": tools.ConvertProtoByteToStr(resp.Payload.Protocol), "startPort": resp.Payload.StartPort, "endPort": resp.Payload.EndPort, "duration": resp.Payload.Duration, diff --git a/cmd/openspa-client/cmd/root.go b/cmd/openspa-client/cmd/root.go index 15ec97a..9e316b1 100644 --- a/cmd/openspa-client/cmd/root.go +++ b/cmd/openspa-client/cmd/root.go @@ -3,10 +3,10 @@ package cmd import ( "bytes" "fmt" + "github.com/greenstatic/openspa/internal/client" "github.com/greenstatic/openspalib" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/greenstatic/openspa/internal/client" "os" ) diff --git a/cmd/openspa-server/cmd/root.go b/cmd/openspa-server/cmd/root.go index 8426fce..e32f124 100644 --- a/cmd/openspa-server/cmd/root.go +++ b/cmd/openspa-server/cmd/root.go @@ -3,10 +3,10 @@ package cmd import ( "bytes" "fmt" + "github.com/greenstatic/openspa/internal/server" "github.com/greenstatic/openspalib" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/greenstatic/openspa/internal/server" "os" ) diff --git a/cmd/openspa-server/cmd/server.go b/cmd/openspa-server/cmd/server.go index cc8ef8e..27d2f43 100644 --- a/cmd/openspa-server/cmd/server.go +++ b/cmd/openspa-server/cmd/server.go @@ -2,16 +2,16 @@ package cmd import ( "errors" - "github.com/greenstatic/openspalib/cryptography" - log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "net" "github.com/greenstatic/openspa/internal/extensionScripts" "github.com/greenstatic/openspa/internal/firewalltracker" "github.com/greenstatic/openspa/internal/ipresolver" "github.com/greenstatic/openspa/internal/ospa" "github.com/greenstatic/openspa/internal/server" + "github.com/greenstatic/openspalib/cryptography" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "net" "os" "os/signal" "syscall" @@ -88,6 +88,8 @@ var serverCmd = &cobra.Command{ } fwState := firewalltracker.Create(es.GetRuleAdd(), es.GetRuleRemove()) + replay := server.ReplayDetect{} + replay.Setup() bindIp := net.ParseIP(viper.GetString("bind")) binPort := uint16(viper.GetInt("port")) @@ -98,7 +100,8 @@ var serverCmd = &cobra.Command{ privKey, pubKey, es, - fwState} + fwState, + &replay} log.WithFields(log.Fields{"bindIp": bindIp, "port": binPort}).Info("Starting server") diff --git a/cmd/openspa-tools/cmd/genClient.go b/cmd/openspa-tools/cmd/genClient.go index b1c88f4..4c2cd0d 100644 --- a/cmd/openspa-tools/cmd/genClient.go +++ b/cmd/openspa-tools/cmd/genClient.go @@ -1,10 +1,10 @@ package cmd import ( + "github.com/greenstatic/openspa/internal/genOspa" "github.com/greenstatic/openspalib/cryptography" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/greenstatic/openspa/internal/genOspa" "os" "path/filepath" ) diff --git a/cmd/openspa-tools/cmd/genServerKey.go b/cmd/openspa-tools/cmd/genServerKey.go index 30f7a22..980607a 100644 --- a/cmd/openspa-tools/cmd/genServerKey.go +++ b/cmd/openspa-tools/cmd/genServerKey.go @@ -3,9 +3,9 @@ package cmd import ( "crypto/x509" "encoding/pem" + "github.com/greenstatic/openspa/internal/genOspa" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/greenstatic/openspa/internal/genOspa" "os" "path/filepath" ) diff --git a/internal/cmdimpl/getip.go b/internal/cmdimpl/getip.go index 6322b53..8530efa 100644 --- a/internal/cmdimpl/getip.go +++ b/internal/cmdimpl/getip.go @@ -2,8 +2,8 @@ package cmdimpl import ( "fmt" - log "github.com/sirupsen/logrus" "github.com/greenstatic/openspa/internal/ipresolver" + log "github.com/sirupsen/logrus" "strconv" ) diff --git a/internal/extensionScripts/ruleAdd.go b/internal/extensionScripts/ruleAdd.go index 32100b7..479fee3 100644 --- a/internal/extensionScripts/ruleAdd.go +++ b/internal/extensionScripts/ruleAdd.go @@ -3,8 +3,8 @@ package extensionScripts import ( "bytes" "errors" - log "github.com/sirupsen/logrus" "github.com/greenstatic/openspa/internal/firewalltracker" + log "github.com/sirupsen/logrus" "os/exec" "strconv" ) diff --git a/internal/extensionScripts/ruleRemove.go b/internal/extensionScripts/ruleRemove.go index 8bb7169..850ff79 100644 --- a/internal/extensionScripts/ruleRemove.go +++ b/internal/extensionScripts/ruleRemove.go @@ -3,8 +3,8 @@ package extensionScripts import ( "bytes" "errors" - log "github.com/sirupsen/logrus" "github.com/greenstatic/openspa/internal/firewalltracker" + log "github.com/sirupsen/logrus" "os/exec" "strconv" ) diff --git a/internal/genOspa/generate.go b/internal/genOspa/generate.go index a2b20c6..ff31a8b 100644 --- a/internal/genOspa/generate.go +++ b/internal/genOspa/generate.go @@ -5,10 +5,10 @@ import ( "crypto/rsa" "crypto/x509" "encoding/pem" - log "github.com/sirupsen/logrus" - "gopkg.in/yaml.v2" "github.com/greenstatic/openspa/internal/client" "github.com/greenstatic/openspa/internal/ospa" + log "github.com/sirupsen/logrus" + "gopkg.in/yaml.v2" ) // Encodes the RSA private key to a PEM formatted byte slice diff --git a/internal/genOspa/walkthrough.go b/internal/genOspa/walkthrough.go index e6d4f5c..4023351 100644 --- a/internal/genOspa/walkthrough.go +++ b/internal/genOspa/walkthrough.go @@ -6,13 +6,13 @@ import ( "crypto/rsa" "errors" "fmt" + "github.com/greenstatic/openspa/internal/ipresolver" + "github.com/greenstatic/openspa/internal/ospa" "github.com/greenstatic/openspalib/cryptography" "github.com/satori/go.uuid" log "github.com/sirupsen/logrus" "io/ioutil" "net" - "github.com/greenstatic/openspa/internal/ipresolver" - "github.com/greenstatic/openspa/internal/ospa" "os" "path/filepath" "regexp" diff --git a/internal/ospa/ospa.go b/internal/ospa/ospa.go index bc7ac24..54fbbc7 100644 --- a/internal/ospa/ospa.go +++ b/internal/ospa/ospa.go @@ -2,12 +2,12 @@ package ospa import ( "errors" + "github.com/greenstatic/openspa/internal/client" + "github.com/greenstatic/openspa/internal/ipresolver" "github.com/satori/go.uuid" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" "net" - "github.com/greenstatic/openspa/internal/client" - "github.com/greenstatic/openspa/internal/ipresolver" "regexp" "strconv" ) diff --git a/internal/server/receiving.go b/internal/server/receiving.go index 5f8fe9d..0f5cb60 100644 --- a/internal/server/receiving.go +++ b/internal/server/receiving.go @@ -3,13 +3,13 @@ package server import ( "crypto/rsa" "errors" + "github.com/greenstatic/openspa/internal/firewalltracker" "github.com/greenstatic/openspalib/cryptography" "github.com/greenstatic/openspalib/request" "github.com/greenstatic/openspalib/response" "github.com/satori/go.uuid" log "github.com/sirupsen/logrus" "net" - "github.com/greenstatic/openspa/internal/firewalltracker" "strconv" "time" ) @@ -89,6 +89,24 @@ func (n *New) processPacket(data []byte) ([]byte, error) { return nil, err } + // Check the timestamp field is valid + if err = expiredPacket(packet); err != nil { + log.WithFields(log.Fields{ + "clientDeviceId": packet.Payload.ClientDeviceID, + "created": packet.Payload.Timestamp.UTC().String(), + }).Warning("Received old packet or the client doesn't have their clock synchronized") + return nil, err + } + + // Replay detection by hashing the packet + if err = n.Replay.Check(data); err != nil { + log.WithFields(log.Fields{ + "clientDeviceId": packet.Payload.ClientDeviceID, + "created": packet.Payload.Timestamp.UTC().String(), + }).Warning("Replay detected - packet already received, discarding packet") + return nil, errors.New("replay detected") + } + clientID := packet.Payload.ClientDeviceID log.WithField("clientDeviceId", clientID). Debug("Getting the user's public key to verify packet") @@ -100,7 +118,12 @@ func (n *New) processPacket(data []byte) ([]byte, error) { return nil, errors.New("failed to get user's public key") } - // TODO - if clientPubKey is null return with no packet data since they are not authorized + // If clientPubKey is null return with no packet data since they are not authorized + if clientPubKey == nil { + log.WithField("clientDeviceId", packet.Payload.ClientDeviceID). + Info("No client public key found, discarding packet") + return nil, errors.New("no client public key found") + } // Verify signature signatureValid := cryptography.RSA_SHA256_signature_verify(packet.ByteData, clientPubKey, packet.Signature) diff --git a/internal/server/replayDetection.go b/internal/server/replayDetection.go new file mode 100644 index 0000000..6891dd0 --- /dev/null +++ b/internal/server/replayDetection.go @@ -0,0 +1,54 @@ +package server + +import ( + "crypto/sha256" + "errors" + "github.com/greenstatic/openspalib/request" + "log" + "sync" + "time" +) + +type ReplayDetect struct { + HashedPackets map[string]bool + mux sync.Mutex +} + +// Discard packets that were generated more than 5 min ago - first line of +// defense against replay defense +func expiredPacket(packet request.Packet) error { + var dur time.Duration = 5 * time.Minute + if time.Now().Sub(packet.Payload.Timestamp) > dur { + return errors.New("packet expired") + } + + return nil +} + +func (rd *ReplayDetect) Setup() { + rd.HashedPackets = make(map[string]bool) +} + +// Checks if the packet has already been sent by taking the SHA-256 hash of +// the packet and comparing it with all the received packets. +func (rd *ReplayDetect) Check(packet []byte) error { + + if rd.HashedPackets == nil { + log.Fatal("ReplayDetect struct was not setup using the Setup() function") + } + + // Take hash of the packet + sum := sha256.Sum256(packet) + sumStr := string(sum[:]) + + rd.mux.Lock() + defer rd.mux.Unlock() + _, found := rd.HashedPackets[sumStr] + if found { + return errors.New("hash of packet found") + } + + rd.HashedPackets[sumStr] = true + + return nil +} diff --git a/internal/server/server.go b/internal/server/server.go index edab01b..2d457c2 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -2,10 +2,10 @@ package server import ( "crypto/rsa" - "github.com/greenstatic/openspalib/request" - "net" "github.com/greenstatic/openspa/internal/extensionScripts" "github.com/greenstatic/openspa/internal/firewalltracker" + "github.com/greenstatic/openspalib/request" + "net" ) const ( @@ -19,4 +19,5 @@ type New struct { PublicKey *rsa.PublicKey ExtensionScripts extensionScripts.Scripts FirewallState *firewalltracker.State + Replay *ReplayDetect } diff --git a/internal/server/version.go b/internal/server/version.go index e30211c..e1f2b51 100644 --- a/internal/server/version.go +++ b/internal/server/version.go @@ -4,6 +4,6 @@ import "fmt" const VersionMajor = 0 const VersionMinor = 1 -const VersionBugfix = 1 +const VersionBugfix = 2 var Version = fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionBugfix)