Skip to content

Commit 142b7dd

Browse files
committed
fix: handle commas in digest challenge values
Reimplement parsing of digest auth challenge to handle cases where the values of key/value pairs contain commas, such as in qop="auth, auth-int"
1 parent 89d25d9 commit 142b7dd

File tree

3 files changed

+67
-32
lines changed

3 files changed

+67
-32
lines changed

client_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ func TestClientDigestAuth(t *testing.T) {
104104
func TestClientDigestSession(t *testing.T) {
105105
conf := defaultDigestServerConf()
106106
conf.algo = "MD5-sess"
107+
conf.qop = "auth, auth-int"
107108
ts := createDigestServer(t, conf)
108109
defer ts.Close()
109110

@@ -134,6 +135,7 @@ func TestClientDigestErrors(t *testing.T) {
134135
{mutateConf: func(c *digestServerConfig) { c.uri = "/bad" }, expect: ErrDigestBadChallenge},
135136
{mutateConf: func(c *digestServerConfig) { c.uri = "/unknown_param" }, expect: ErrDigestBadChallenge},
136137
{mutateConf: func(c *digestServerConfig) { c.uri = "/missing_value" }, expect: ErrDigestBadChallenge},
138+
{mutateConf: func(c *digestServerConfig) { c.uri = "/unclosed_quote" }, expect: ErrDigestBadChallenge},
137139
{mutateConf: func(c *digestServerConfig) { c.uri = "/no_challenge" }, expect: ErrDigestBadChallenge},
138140
{mutateConf: func(c *digestServerConfig) { c.uri = "/status_500" }, expect: nil},
139141
}

digest.go

+62-32
Original file line numberDiff line numberDiff line change
@@ -125,48 +125,78 @@ type challenge struct {
125125
userhash string
126126
}
127127

128+
func (c *challenge) setValue(k, v string) error {
129+
switch k {
130+
case "realm":
131+
c.realm = v
132+
case "domain":
133+
c.domain = v
134+
case "nonce":
135+
c.nonce = v
136+
case "opaque":
137+
c.opaque = v
138+
case "stale":
139+
c.stale = v
140+
case "algorithm":
141+
c.algorithm = v
142+
case "qop":
143+
c.qop = v
144+
case "charset":
145+
if strings.ToUpper(v) != "UTF-8" {
146+
return ErrDigestCharset
147+
}
148+
case "userhash":
149+
c.userhash = v
150+
default:
151+
return ErrDigestBadChallenge
152+
}
153+
return nil
154+
}
155+
128156
func parseChallenge(input string) (*challenge, error) {
129157
const ws = " \n\r\t"
130-
const qs = `"`
131158
s := strings.Trim(input, ws)
132159
if !strings.HasPrefix(s, "Digest ") {
133160
return nil, ErrDigestBadChallenge
134161
}
135162
s = strings.Trim(s[7:], ws)
136-
sl := strings.Split(s, ",")
137163
c := &challenge{}
138-
var r []string
139-
for i := range sl {
140-
sl[i] = strings.TrimSpace(sl[i])
141-
r = strings.SplitN(sl[i], "=", 2)
142-
if len(r) != 2 {
143-
return nil, ErrDigestBadChallenge
144-
}
145-
r[0] = strings.TrimSpace(r[0])
146-
r[1] = strings.TrimSpace(r[1])
147-
switch r[0] {
148-
case "realm":
149-
c.realm = strings.Trim(r[1], qs)
150-
case "domain":
151-
c.domain = strings.Trim(r[1], qs)
152-
case "nonce":
153-
c.nonce = strings.Trim(r[1], qs)
154-
case "opaque":
155-
c.opaque = strings.Trim(r[1], qs)
156-
case "stale":
157-
c.stale = strings.Trim(r[1], qs)
158-
case "algorithm":
159-
c.algorithm = strings.Trim(r[1], qs)
160-
case "qop":
161-
c.qop = strings.Trim(r[1], qs)
162-
case "charset":
163-
if strings.ToUpper(strings.Trim(r[1], qs)) != "UTF-8" {
164-
return nil, ErrDigestCharset
164+
b := strings.Builder{}
165+
key := ""
166+
quoted := false
167+
for _, r := range s {
168+
switch r {
169+
case '"':
170+
quoted = !quoted
171+
case ',':
172+
if quoted {
173+
b.WriteRune(r)
174+
} else {
175+
val := strings.Trim(b.String(), ws)
176+
b.Reset()
177+
if err := c.setValue(key, val); err != nil {
178+
return nil, err
179+
}
180+
key = ""
181+
}
182+
case '=':
183+
if quoted {
184+
b.WriteRune(r)
185+
} else {
186+
key = strings.Trim(b.String(), ws)
187+
b.Reset()
165188
}
166-
case "userhash":
167-
c.userhash = strings.Trim(r[1], qs)
168189
default:
169-
return nil, ErrDigestBadChallenge
190+
b.WriteRune(r)
191+
}
192+
}
193+
if quoted || (key == "" && b.Len() > 0) {
194+
return nil, ErrDigestBadChallenge
195+
}
196+
if key != "" {
197+
val := strings.Trim(b.String(), ws)
198+
if err := c.setValue(key, val); err != nil {
199+
return nil, err
170200
}
171201
}
172202
return c, nil

resty_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,9 @@ func createDigestServer(t *testing.T, conf *digestServerConfig) *httptest.Server
700700
case "/missing_value":
701701
setWWWAuthHeader(w, `Digest realm="hello", domain`)
702702
return
703+
case "/unclosed_quote":
704+
setWWWAuthHeader(w, `Digest realm="hello, qop=auth`)
705+
return
703706
case "/no_challenge":
704707
setWWWAuthHeader(w, "")
705708
return

0 commit comments

Comments
 (0)