Skip to content

Commit

Permalink
fix: client panic on missing digest value
Browse files Browse the repository at this point in the history
After splitting key=value pairs, we check that there are really key AND
value. A maliciously crafted response header can cause the client to
panic.
  • Loading branch information
segevda committed Mar 20, 2023
1 parent 38b1644 commit c3ae2b7
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 8 deletions.
1 change: 1 addition & 0 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ func TestClientDigestErrors(t *testing.T) {
{mutateConf: func(c *digestServerConfig) { c.charset = "utf-16" }, expect: ErrDigestCharset},
{mutateConf: func(c *digestServerConfig) { c.uri = "/bad" }, expect: ErrDigestBadChallenge},
{mutateConf: func(c *digestServerConfig) { c.uri = "/unknown_param" }, expect: ErrDigestBadChallenge},
{mutateConf: func(c *digestServerConfig) { c.uri = "/missing_value" }, expect: ErrDigestBadChallenge},
{mutateConf: func(c *digestServerConfig) { c.uri = "/no_challenge" }, expect: ErrDigestBadChallenge},
{mutateConf: func(c *digestServerConfig) { c.uri = "/status_500" }, expect: nil},
}
Expand Down
3 changes: 3 additions & 0 deletions digest.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ func parseChallenge(input string) (*challenge, error) {
var r []string
for i := range sl {
r = strings.SplitN(sl[i], "=", 2)
if len(r) != 2 {
return nil, ErrDigestBadChallenge
}
switch r[0] {
case "realm":
c.realm = strings.Trim(r[1], qs)
Expand Down
20 changes: 12 additions & 8 deletions resty_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -645,22 +645,27 @@ func createDigestServer(t *testing.T, conf *digestServerConfig) *httptest.Server
if conf == nil {
conf = defaultDigestServerConf()
}

setWWWAuthHeader := func(w http.ResponseWriter, v string) {
w.Header().Set("WWW-Authenticate", v)
w.WriteHeader(http.StatusUnauthorized)
}
ts := createTestServer(func(w http.ResponseWriter, r *http.Request) {
t.Logf("Method: %v", r.Method)
t.Logf("Path: %v", r.URL.Path)

switch r.URL.Path {
case "/bad":
w.Header().Set("WWW-Authenticate", "Bad Challenge")
w.WriteHeader(http.StatusUnauthorized)
setWWWAuthHeader(w, "Bad Challenge")
return
case "/unknown_param":
w.Header().Set("WWW-Authenticate", "Digest unknown_param=true")
w.WriteHeader(http.StatusUnauthorized)
setWWWAuthHeader(w, "Digest unknown_param=true")
return
case "/missing_value":
setWWWAuthHeader(w, `Digest realm="hello", domain`)
return
case "/no_challenge":
w.Header().Set("WWW-Authenticate", "")
w.WriteHeader(http.StatusUnauthorized)
setWWWAuthHeader(w, "")
return
case "/status_500":
w.WriteHeader(http.StatusInternalServerError)
Expand All @@ -670,10 +675,9 @@ func createDigestServer(t *testing.T, conf *digestServerConfig) *httptest.Server
w.Header().Set(hdrContentTypeKey, "application/json; charset=utf-8")

if !authorizationHeaderValid(t, r, conf) {
w.Header().Set("WWW-Authenticate",
setWWWAuthHeader(w,
fmt.Sprintf(`Digest realm="%s", domain="%s", qop="%s", algorithm=%s, nonce="%s", opaque="%s", userhash=true, charset=%s, stale=FALSE`,
conf.realm, conf.uri, conf.qop, conf.algo, conf.nonce, conf.opaque, conf.charset))
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte(`{ "id": "unauthorized", "message": "Invalid credentials" }`))
} else {
w.WriteHeader(http.StatusOK)
Expand Down

0 comments on commit c3ae2b7

Please sign in to comment.