Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Adjustments & fixes enabling ZooKeeper DIGEST-MD5 authentication #3

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions Mechanisms/SaslDigestMd5.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,20 @@ string Password {
}
}

/// <summary>
/// The protocol to use. If none is specified, this
/// implementation uses "imap".
/// </summary>
string Protocol {
get {
return Properties.ContainsKey("Protocol") ?
Properties["Protocol"] as string : null;
}
set {
Properties["Protocol"] = value;
}
}

/// <summary>
/// Private constructor for use with Sasl.SaslFactory.
/// </summary>
Expand Down Expand Up @@ -151,10 +165,22 @@ private byte[] ComputeDigestResponse(byte[] challenge) {
// Parse the challenge-string and construct the "response-value" from it.
string decoded = Encoding.ASCII.GetString(challenge);
NameValueCollection fields = ParseDigestChallenge(decoded);
string digestUri = "imap/" + fields["realm"];
string protocol = Protocol;
if (string.IsNullOrEmpty(protocol))
protocol = "imap";
string digestUri = protocol + "/" + fields["realm"];
string responseValue = ComputeDigestResponseValue(fields, Cnonce, digestUri,
Username, Password);

// RFC 2831 says that qop is optional, and Java's SASL
// impl chokes on empty directives. Let's specify the
// default, which is 'auth'.
//
// javax.security.sasl.SaslException: Valueless directive found: qop
string qop = fields["qop"];
if (string.IsNullOrEmpty(qop))
qop = "auth";

// Create the challenge-response string.
string[] directives = new string[] {
// We don't use UTF-8 in the current implementation.
Expand All @@ -166,7 +192,7 @@ private byte[] ComputeDigestResponse(byte[] challenge) {
"cnonce=" + Dquote(Cnonce),
"digest-uri=" + Dquote(digestUri),
"response=" + responseValue,
"qop=" + fields["qop"]
"qop=" + qop
};
string challengeResponse = String.Join(",", directives);
// Finally, return the response as a byte array.
Expand Down Expand Up @@ -229,10 +255,14 @@ private static string ComputeDigestResponseValue(NameValueCollection challenge,
cnonce;
// Construct A2.
string A2 = "AUTHENTICATE:" + digestUri;
if (!"auth".Equals(challenge["qop"]))
string qop = challenge["qop"];
// RFC 2831: If not present, [qop] defaults to "auth".
if (string.IsNullOrEmpty(qop))
qop = "auth";
if (!"auth".Equals(qop))
A2 = A2 + ":00000000000000000000000000000000";
string ret = MD5(A1, enc) + ":" + challenge["nonce"] + ":" + ncValue +
":" + cnonce + ":" + challenge["qop"] + ":" + MD5(A2, enc);
":" + cnonce + ":" + qop + ":" + MD5(A2, enc);
return MD5(ret, enc);
}
}
Expand Down