Closed scottlamb closed 3 years ago
Hmm, servers are allowed to specify more than one challenge as a comma-separated list (or as multiple headers, which rtsp-types
converts to the comma-separated form here). Ideally we would support that and pick out digest if it's present, then basic if it's present, then err out otherwise.
Splitting the WWW-Authenticate
header into challenges is more than header_value.split(", ")
because ,
can occur in a quoted string. See the grammar from RFC 7235 Appendix C:
WWW-Authenticate = *( "," OWS ) challenge *( OWS "," [ OWS challenge
] )
auth-param = token BWS "=" BWS ( token / quoted-string )
auth-scheme = token
challenge = auth-scheme [ 1*SP ( token68 / [ ( "," / auth-param ) *(
OWS "," [ OWS auth-param ] ) ] ) ]
credentials = auth-scheme [ 1*SP ( token68 / [ ( "," / auth-param )
*( OWS "," [ OWS auth-param ] ) ] ) ]
quoted-string = <quoted-string, see [RFC7230], Section 3.2.6>
token = <token, see [RFC7230], Section 3.2.6>
token68 = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" )
*"="
We could parse to split it properly, then give the digest part to digest_auth
to parse again, but that's annoying to parse twice.
@lucaszanella you were working to polish up www-authenticate
(KeenS/www-authenticate#4). Do you want to finish that? It'd parse everything in one go.
I'm assuming from the name digest_auth
that its author isn't interested in expanding it to parse other challenge types like basic.
The Anpviz IPC-D250 is just sending this:
WWW-Authenticate: Basic realm="/"
as mentioned above. It wouldn't be hard to directly hack support for this simple case into Retina, but I'm trying to look forward a bit to make sure we're doing the right thing on cameras which may offer multiple challenges. The www-authenticate
crate (with your changes to get rid of those unsound-looking transmutes) is probably not far off from doing this properly.
Eg my Hikvision cameras support this:
WWW-Authenticate: Digest realm="8ce748da948f", nonce="9d6f3162baa241e62a3c134c0acf678b", stale="FALSE"
WWW-Authenticate: Basic realm="8ce748da948f"
which I believe rtsp-types
transforms into the equivalent:
WWW-Authenticate: Digest realm="8ce748da948f", nonce="9d6f3162baa241e62a3c134c0acf678b", stale="FALSE", Basic realm="8ce748da948f"
Retina currently works correctly with this, but will probably fail if the headers were in the reverse order. It doesn't support Basic
, and it doesn't know how to parse multiple challenges or that it should prefer the stronger Digest
.
I'm confused. Shouldn't these 2 WWW-Authenticate
be treated separately and sent to www-authenticate?
Also,
fn test_parse_crazy_answer() {
let b = b"Basic realm=\"/\"";
let stream = Stream::new(b);
match stream.challenge().unwrap() {
(_, RawChallenge::Token68(_)) => panic!(),
(scheme, RawChallenge::Fields(fields)) => {
assert_eq!(scheme, "Basic");
}
}
assert!(stream.is_end());
}
works on my crate
I'm confused. Shouldn't these 2
WWW-Authenticate
be treated separately and sent to www-authenticate?
RFC 2636 section 4.2 refers to RFC 2616 section 4.2, which says the following:
Multiple message-header fields with the same field-name MAY be
present in a message if and only if the entire field-value for that
header field is defined as a comma-separated list [i.e., #(values)].
It MUST be possible to combine the multiple header fields into one
"field-name: field-value" pair, without changing the semantics of the
message, by appending each subsequent field-value to the first, each
separated by a comma. The order in which header fields with the same
field-name are received is therefore significant to the
interpretation of the combined field value, and thus a proxy MUST NOT
change the order of these field values when a message is forwarded.
and rtsp-types
combines it that way, with the code I linked above.
From https://github.com/scottlamb/moonfire-nvr/discussions/151. When configured with username and password, Anpviz IPC-D250 reports: