Skip to content

Commit

Permalink
split headers.Authenticate and headers.Authorization
Browse files Browse the repository at this point in the history
  • Loading branch information
aler9 committed Feb 22, 2024
1 parent c93d5c5 commit b46a92c
Show file tree
Hide file tree
Showing 21 changed files with 367 additions and 422 deletions.
12 changes: 8 additions & 4 deletions client_play_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1691,12 +1691,13 @@ func TestClientPlayRedirect(t *testing.T) {
authOpaque := "exampleOpaque"
authStale := "FALSE"
authAlg := "MD5"

err = conn.WriteResponse(&base.Response{
Header: base.Header{
"WWW-Authenticate": headers.Authenticate{
Method: headers.AuthDigest,
Realm: &authRealm,
Nonce: &authNonce,
Realm: authRealm,
Nonce: authNonce,
Opaque: &authOpaque,
Stale: &authStale,
Algorithm: &authAlg,
Expand All @@ -1706,13 +1707,16 @@ func TestClientPlayRedirect(t *testing.T) {
})
require.NoError(t, err)
}

req, err = conn.ReadRequest()
require.NoError(t, err)

authHeaderVal, exists := req.Header["Authorization"]
require.True(t, exists)
var authHeader headers.Authenticate

var authHeader headers.Authorization
require.NoError(t, authHeader.Unmarshal(authHeaderVal))
require.Equal(t, *authHeader.Username, "testusr")
require.Equal(t, authHeader.Username, "testusr")
require.Equal(t, base.Describe, req.Method)
}

Expand Down
60 changes: 18 additions & 42 deletions pkg/auth/sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@ func findHeader(v base.HeaderValue, prefix string) string {

// Sender allows to send credentials.
type Sender struct {
user string
pass string
method headers.AuthMethod
realm string
nonce string
user string
pass string
authenticateHeader *headers.Authenticate
}

// NewSender allocates a Sender.
Expand All @@ -38,20 +36,10 @@ func NewSender(v base.HeaderValue, user string, pass string) (*Sender, error) {
return nil, err
}

if auth.Realm == nil {
return nil, fmt.Errorf("realm is missing")
}

if auth.Nonce == nil {
return nil, fmt.Errorf("nonce is missing")
}

return &Sender{
user: user,
pass: pass,
method: headers.AuthDigest,
realm: *auth.Realm,
nonce: *auth.Nonce,
user: user,
pass: pass,
authenticateHeader: &auth,
}, nil
}

Expand All @@ -62,15 +50,10 @@ func NewSender(v base.HeaderValue, user string, pass string) (*Sender, error) {
return nil, err
}

if auth.Realm == nil {
return nil, fmt.Errorf("realm is missing")
}

return &Sender{
user: user,
pass: pass,
method: headers.AuthBasic,
realm: *auth.Realm,
user: user,
pass: pass,
authenticateHeader: &auth,
}, nil
}

Expand All @@ -82,26 +65,19 @@ func (se *Sender) AddAuthorization(req *base.Request) {
urStr := req.URL.CloneWithoutCredentials().String()

h := headers.Authorization{
Method: se.method,
Method: se.authenticateHeader.Method,
}

switch se.method {
case headers.AuthBasic:
if se.authenticateHeader.Method == headers.AuthBasic {
h.BasicUser = se.user
h.BasicPass = se.pass

default: // headers.AuthDigest
response := md5Hex(md5Hex(se.user+":"+se.realm+":"+se.pass) + ":" +
se.nonce + ":" + md5Hex(string(req.Method)+":"+urStr))

h.DigestValues = headers.Authenticate{
Method: headers.AuthDigest,
Username: &se.user,
Realm: &se.realm,
Nonce: &se.nonce,
URI: &urStr,
Response: &response,
}
} else { // digest
h.Username = se.user
h.Realm = se.authenticateHeader.Realm
h.Nonce = se.authenticateHeader.Nonce
h.URI = urStr
h.Response = md5Hex(md5Hex(se.user+":"+se.authenticateHeader.Realm+":"+se.pass) + ":" +
se.authenticateHeader.Nonce + ":" + md5Hex(string(req.Method)+":"+urStr))
}

if req.Header == nil {
Expand Down
55 changes: 11 additions & 44 deletions pkg/auth/sender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,18 @@ package auth
import (
"testing"

"github.com/stretchr/testify/require"

"github.com/bluenviron/gortsplib/v4/pkg/base"
)

func TestSenderErrors(t *testing.T) {
for _, ca := range []struct {
name string
hv base.HeaderValue
err string
}{
{
"invalid method",
base.HeaderValue{`Invalid`},
"no authentication methods available",
},
{
"digest invalid",
base.HeaderValue{`Digest`},
"unable to split between method and keys (Digest)",
},
{
"digest, missing realm",
base.HeaderValue{`Digest nonce=123`},
"realm is missing",
},
{
"digest, missing nonce",
base.HeaderValue{`Digest realm=123`},
"nonce is missing",
},
{
"basic invalid",
base.HeaderValue{`Basic`},
"unable to split between method and keys (Basic)",
},
{
"basic, missing realm",
base.HeaderValue{`Basic nonce=123`},
"realm is missing",
},
} {
t.Run(ca.name, func(t *testing.T) {
_, err := NewSender(ca.hv, "myuser", "mypass")
require.EqualError(t, err, ca.err)
})
}
func FuzzSender(f *testing.F) {
f.Add(`Invalid`)
f.Add(`Digest`)
f.Add(`Digest nonce=123`)
f.Add(`Digest realm=123`)
f.Add(`Basic`)
f.Add(`Basic nonce=123`)

f.Fuzz(func(t *testing.T, a string) {
NewSender(base.HeaderValue{a}, "myuser", "mypass") //nolint:errcheck
})
}
38 changes: 9 additions & 29 deletions pkg/auth/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ func GenerateWWWAuthenticate(methods []headers.AuthMethod, realm string, nonce s
case headers.AuthBasic:
ret = append(ret, (&headers.Authenticate{
Method: headers.AuthBasic,
Realm: &realm,
Realm: realm,
}).Marshal()...)

case headers.AuthDigest:
ret = append(ret, headers.Authenticate{
Method: headers.AuthDigest,
Realm: &realm,
Nonce: &nonce,
Realm: realm,
Nonce: nonce,
}.Marshal()...)
}
}
Expand Down Expand Up @@ -92,46 +92,26 @@ func Validate(
return fmt.Errorf("authentication failed")
}
case auth.Method == headers.AuthDigest && contains(methods, headers.AuthDigest):
if auth.DigestValues.Realm == nil {
return fmt.Errorf("realm is missing")
}

if auth.DigestValues.Nonce == nil {
return fmt.Errorf("nonce is missing")
}

if auth.DigestValues.Username == nil {
return fmt.Errorf("username is missing")
}

if auth.DigestValues.URI == nil {
return fmt.Errorf("uri is missing")
}

if auth.DigestValues.Response == nil {
return fmt.Errorf("response is missing")
}

if *auth.DigestValues.Nonce != nonce {
if auth.Nonce != nonce {
return fmt.Errorf("wrong nonce")
}

if *auth.DigestValues.Realm != realm {
if auth.Realm != realm {
return fmt.Errorf("wrong realm")
}

if *auth.DigestValues.Username != user {
if auth.Username != user {
return fmt.Errorf("authentication failed")
}

ur := req.URL

if *auth.DigestValues.URI != ur.String() {
if auth.URI != ur.String() {
// in SETUP requests, VLC strips the control attribute.
// try again with the base URL.
if baseURL != nil {
ur = baseURL
if *auth.DigestValues.URI != ur.String() {
if auth.URI != ur.String() {
return fmt.Errorf("wrong URL")
}
} else {
Expand All @@ -142,7 +122,7 @@ func Validate(
response := md5Hex(md5Hex(user+":"+realm+":"+pass) +
":" + nonce + ":" + md5Hex(string(req.Method)+":"+ur.String()))

if *auth.DigestValues.Response != response {
if auth.Response != response {
return fmt.Errorf("authentication failed")
}
default:
Expand Down
114 changes: 47 additions & 67 deletions pkg/auth/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,75 +3,55 @@ package auth
import (
"testing"

"github.com/stretchr/testify/require"

"github.com/bluenviron/gortsplib/v4/pkg/base"
"github.com/bluenviron/gortsplib/v4/pkg/headers"
"github.com/stretchr/testify/require"
)

func TestValidateErrors(t *testing.T) {
for _, ca := range []struct {
name string
hv base.HeaderValue
err string
}{
{
"invalid auth",
base.HeaderValue{`Invalid`},
"invalid authorization header",
},
{
"digest missing realm",
base.HeaderValue{`Digest `},
"realm is missing",
},
{
"digest missing nonce",
base.HeaderValue{`Digest realm=123`},
"nonce is missing",
},
{
"digest missing username",
base.HeaderValue{`Digest realm=123,nonce=123`},
"username is missing",
},
{
"digest missing uri",
base.HeaderValue{`Digest realm=123,nonce=123,username=123`},
"uri is missing",
},
{
"digest missing response",
base.HeaderValue{`Digest realm=123,nonce=123,username=123,uri=123`},
"response is missing",
},
{
"digest wrong nonce",
base.HeaderValue{`Digest realm=123,nonce=123,username=123,uri=123,response=123`},
"wrong nonce",
},
{
"digest wrong realm",
base.HeaderValue{`Digest realm=123,nonce=abcde,username=123,uri=123,response=123`},
"wrong realm",
},
} {
t.Run(ca.name, func(t *testing.T) {
err := Validate(
&base.Request{
Method: base.Describe,
URL: nil,
Header: base.Header{
"Authorization": ca.hv,
},
func FuzzValidate(f *testing.F) {
f.Add(`Invalid`)
f.Add(`Digest `)
f.Add(`Digest realm=123`)
f.Add(`Digest realm=123,nonce=123`)
f.Add(`Digest realm=123,nonce=123,username=123`)
f.Add(`Digest realm=123,nonce=123,username=123,uri=123`)
f.Add(`Digest realm=123,nonce=123,username=123,uri=123,response=123`)
f.Add(`Digest realm=123,nonce=abcde,username=123,uri=123,response=123`)

f.Fuzz(func(t *testing.T, a string) {
Validate( //nolint:errcheck
&base.Request{
Method: base.Describe,
URL: nil,
Header: base.Header{
"Authorization": base.HeaderValue{a},
},
"myuser",
"mypass",
nil,
nil,
"IPCAM",
"abcde",
)
require.EqualError(t, err, ca.err)
})
}
},
"myuser",
"mypass",
nil,
nil,
"IPCAM",
"abcde",
)
})
}

func TestValidateAdditionalErrors(t *testing.T) {
err := Validate(
&base.Request{
Method: base.Describe,
URL: nil,
Header: base.Header{
"Authorization": base.HeaderValue{"Basic bXl1c2VyOm15cGFzcw=="},
},
},
"myuser",
"mypass",
nil,
[]headers.AuthMethod{headers.AuthDigest},
"IPCAM",
"abcde",
)
require.Error(t, err)
}
Loading

0 comments on commit b46a92c

Please sign in to comment.