From a077bc201a8779cd29f642256a0b9cf9b1fea708 Mon Sep 17 00:00:00 2001 From: Nevio Vesic Date: Thu, 7 Nov 2019 03:37:51 +0100 Subject: [PATCH] Allow setting local credentials via AgentConfig Add local credential support to the AgentConfig and validate credentials per RFC standard. If no credentials are passed we still generate random ones ourselves. --- README.md | 1 + agent.go | 52 ++++++++++++++++++++++++++++++++++++++------------- agent_test.go | 30 +++++++++++++++++++++++++++++ errors.go | 8 ++++++++ go.sum | 1 + 5 files changed, 79 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 81c2bf5..66c20e7 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contribu * [Aaron France](https://github.com/AeroNotix) * [Chao Yuan](https://github.com/yuanchao0310) * [Jason Maldonis](https://github.com/jjmaldonis) +* [Nevio Vesic](https://github.com/0x19) ### License MIT License - see [LICENSE](LICENSE) for full text diff --git a/agent.go b/agent.go index adc9bfe..2e55380 100644 --- a/agent.go +++ b/agent.go @@ -185,6 +185,13 @@ type AgentConfig struct { PortMin uint16 PortMax uint16 + // LocalUfrag and LocalPwd values used to perform connectivity + // checks. The values MUST be unguessable, with at least 128 bits of + // random number generator output used to generate the password, and + // at least 24 bits of output to generate the username fragment. + LocalUfrag string + LocalPwd string + // Trickle specifies whether or not ice agent should trickle candidates or // work perform synchronous gathering. Trickle bool @@ -310,6 +317,26 @@ func NewAgent(config *AgentConfig) (*Agent, error) { return nil, ErrPort } + // local username fragment and password + localUfrag := randSeq(16) + localPwd := randSeq(32) + + if config.LocalUfrag != "" { + if len([]rune(config.LocalUfrag))*8 < 24 { + return nil, ErrLocalUfragInsufficientBits + } + + localUfrag = config.LocalUfrag + } + + if config.LocalPwd != "" { + if len([]rune(config.LocalPwd))*8 < 128 { + return nil, ErrLocalPwdInsufficientBits + } + + localPwd = config.LocalPwd + } + mDNSName, err := generateMulticastDNSName() if err != nil { return nil, err @@ -350,19 +377,18 @@ func NewAgent(config *AgentConfig) (*Agent, error) { checklist: make([]*candidatePair, 0), urls: config.Urls, networkTypes: config.NetworkTypes, - - localUfrag: randSeq(16), - localPwd: randSeq(32), - taskChan: make(chan task), - onConnected: make(chan struct{}), - buffer: packetio.NewBuffer(), - done: make(chan struct{}), - portmin: config.PortMin, - portmax: config.PortMax, - trickle: config.Trickle, - loggerFactory: loggerFactory, - log: log, - net: config.Net, + localUfrag: localUfrag, + localPwd: localPwd, + taskChan: make(chan task), + onConnected: make(chan struct{}), + buffer: packetio.NewBuffer(), + done: make(chan struct{}), + portmin: config.PortMin, + portmax: config.PortMax, + trickle: config.Trickle, + loggerFactory: loggerFactory, + log: log, + net: config.Net, mDNSMode: mDNSMode, mDNSName: mDNSName, diff --git a/agent_test.go b/agent_test.go index 8d6b97b..6be48c3 100644 --- a/agent_test.go +++ b/agent_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/pion/logging" "github.com/pion/stun" "github.com/pion/transport/test" "github.com/pion/transport/vnet" @@ -1231,3 +1232,32 @@ func TestBindingRequestTimeout(t *testing.T) { assert.Equal(t, len(a.pendingBindingRequests), expectedRemovalCount, "Binding invalidation due to timeout did not remove the correct number of binding requests") assert.NoError(t, a.Close()) } + +// TestAgentCredentials checks if local username fragments and passwords (if set) meet RFC standard +// and ensure it's backwards compatible with previous versions of the pion/ice +func TestAgentCredentials(t *testing.T) { + + // Make sure to pass travis check by disabling the logs + log := logging.NewDefaultLoggerFactory() + log.DefaultLogLevel = logging.LogLevelDisabled + + // Agent should not require any of the usernames and password to be set + // If set, they should follow the default 16/128 bits random number generator strategy + + agent, err := NewAgent(&AgentConfig{Trickle: true, LoggerFactory: log}) + assert.NoError(t, err) + assert.GreaterOrEqual(t, len([]rune(agent.localUfrag))*8, 24) + assert.GreaterOrEqual(t, len([]rune(agent.localPwd))*8, 128) + assert.NoError(t, agent.Close()) + + // Should honor RFC standards + // Local values MUST be unguessable, with at least 128 bits of + // random number generator output used to generate the password, and + // at least 24 bits of output to generate the username fragment. + + _, err = NewAgent(&AgentConfig{Trickle: true, LocalUfrag: "xx", LoggerFactory: log}) + assert.EqualError(t, err, ErrLocalUfragInsufficientBits.Error()) + + _, err = NewAgent(&AgentConfig{Trickle: true, LocalPwd: "xxxxxx", LoggerFactory: log}) + assert.EqualError(t, err, ErrLocalPwdInsufficientBits.Error()) +} diff --git a/errors.go b/errors.go index ae90de9..edc34b8 100644 --- a/errors.go +++ b/errors.go @@ -21,6 +21,14 @@ var ( // ErrPort indicates malformed port is provided. ErrPort = errors.New("invalid port") + // ErrLocalUfragInsufficientBits indicates local username fragment insufficient bits are provided. + // Have to be at least 24 bits long + ErrLocalUfragInsufficientBits = errors.New("local username fragment is less than 24 bits long") + + // ErrLocalPwdInsufficientBits indicates local passoword insufficient bits are provided. + // Have to be at least 128 bits long + ErrLocalPwdInsufficientBits = errors.New("local password is less than 128 bits long") + // ErrProtoType indicates an unsupported transport type was provided. ErrProtoType = errors.New("invalid transport protocol type") diff --git a/go.sum b/go.sum index 7a13fd2..7bc68f7 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b h1:lkjdUzSyJ5P1+eal9fxXX9Xg2BTfswsonKUse48C0uE= golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9 h1:DPz9iiH3YoKiKhX/ijjoZvT0VFwK2c6CWYWQ7Zyr8TU= golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=