Digest authentication support

git-svn-id: svn://ultimatepp.org/upp/trunk@1962 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
rylek 2010-01-27 19:24:29 +00:00
parent 16d45496d3
commit 051004a13c
4 changed files with 116 additions and 1 deletions

View file

@ -19,6 +19,7 @@ void HttpClient::Init()
max_header_size = DEFAULT_MAX_HEADER_SIZE;
max_content_size = DEFAULT_MAX_CONTENT_SIZE;
keepalive = false;
force_digest = false;
std_headers = true;
hasurlvar = false;
method = METHOD_GET;
@ -78,6 +79,14 @@ String HttpClient::ExecuteRedirect(int max_redirect, int retries, Gate2<int, int
return String::GetVoid();
}
String data = Execute(progress);
if(status_code == 401 && !IsNull(username) && !IsNull(authenticate)) {
if(++nredir > max_redirect) {
error = NFormat("Maximum number of digest authentication attempts exceeded: %d", max_redirect);
return String::GetVoid();
}
Digest(CalculateDigest(authenticate));
continue;
}
if(status_code >= 400 && status_code < 500) {
error = status_line;
return String::GetVoid();
@ -146,6 +155,81 @@ HttpClient& HttpClient::UrlVar(const char *id, const String& data)
return *this;
}
String HttpClient::CalculateDigest(String authenticate) const
{
const char *p = authenticate;
String realm, qop, nonce, opaque;
while(*p) {
if(!IsAlNum(*p)) {
p++;
continue;
}
else {
const char *b = p;
while(IsAlNum(*p))
p++;
String var = ToLower(String(b, p));
String value;
while(*p && (byte)*p <= ' ')
p++;
if(*p == '=') {
p++;
while(*p && (byte)*p <= ' ')
p++;
if(*p == '\"') {
p++;
while(*p && *p != '\"')
if(*p != '\\' || *++p)
value.Cat(*p++);
if(*p == '\"')
p++;
}
else {
b = p;
while(*p && *p != ',' && (byte)*p > ' ')
p++;
value = String(b, p);
}
}
if(var == "realm")
realm = value;
else if(var == "qop")
qop = value;
else if(var == "nonce")
nonce = var;
else if(var == "opaque")
opaque = var;
}
}
String hv1, hv2;
hv1 << username << ':' << realm << ':' << password;
String ha1 = MD5String(hv1);
hv2 << (method == METHOD_GET ? "GET" : method == METHOD_POST ? "POST" : "READ")
<< ':' << path;
String ha2 = MD5String(hv2);
int nc = 1;
String cnonce = FormatIntHex(Random(), 8);
String hv;
hv << BinhexEncode(ha1)
<< ':' << nonce
<< ':' << FormatIntHex(nc, 8)
<< ':' << cnonce
<< ':' << qop << ':' << BinhexEncode(ha2);
String ha = MD5String(hv);
String auth;
auth << "Authorization: Digest "
"username=" << AsCString(username)
<< ", realm=" << AsCString(realm)
<< ", nonce=" << AsCString(nonce)
<< ", uri=" << AsCString(path)
<< ", qop=" << AsCString(qop)
<< ", nc=" << AsCString(FormatIntHex(nc, 8))
<< ", cnonce=" << cnonce
<< ", response=" << AsCString(BinhexEncode(ha))
<< ", opaque=" << AsCString(opaque);
return auth;
}
String HttpClient::Execute(Gate2<int, int> progress)
{
LLOGBLOCK("HttpClient::Execute");
@ -222,7 +306,9 @@ String HttpClient::Execute(Gate2<int, int> progress)
}
if(use_proxy && !IsNull(proxy_username))
request << "Proxy-Authorization: basic " << Base64Encode(proxy_username + ':' + proxy_password) << "\r\n";
if(!IsNull(username) || !IsNull(password))
if(!IsNull(digest))
request << "Authorization: Digest " << digest << "\r\n";
else if(!force_digest && (!IsNull(username) || !IsNull(password)))
request << "Authorization: basic " << Base64Encode(username + ":" + password) << "\r\n";
request << client_headers << "\r\n" << postdata;
LLOG("host = " << host << ", port = " << port);
@ -285,10 +371,12 @@ String HttpClient::Execute(Gate2<int, int> progress)
static const char ce[] = "content-encoding:";
static const char te[] = "transfer-encoding:";
static const char lo[] = "location:";
static const char au[] = "www-authenticate:";
static const int CL_LENGTH = sizeof(cl) - 1;
static const int CE_LENGTH = sizeof(ce) - 1;
static const int TE_LENGTH = sizeof(te) - 1;
static const int LO_LENGTH = sizeof(lo) - 1;
static const int AU_LENGTH = sizeof(au) - 1;
if(!MemICmp(p, cl, CL_LENGTH)) {
for(p += CL_LENGTH; *p == ' '; p++)
;
@ -323,6 +411,11 @@ String HttpClient::Execute(Gate2<int, int> progress)
if(p >= 0 && q < 0)
redirect_url.Cat(path.GetIter(p));
}
else if(!MemICmp(p, au, AU_LENGTH)) {
for(p += LO_LENGTH; *p == ' '; p++)
;
authenticate = String(p, e);
}
if(server_headers.GetLength() + (e - b) + 2 > max_header_size) {
error = NFormat(t_("%s:%d: maximum header length exceeded (%d B)"), host, port, max_header_size);
return String::GetVoid();

View file

@ -15,6 +15,8 @@ public:
HttpClient& Port(int p) { port = p; return *this; }
HttpClient& Path(String p) { path = p; return *this; }
HttpClient& User(String u, String p) { username = u; password = p; return *this; }
HttpClient& Digest() { force_digest = true; return *this; }
HttpClient& Digest(String d) { digest = d; return *this; }
HttpClient& URL(const char *url);
HttpClient& Url(const char *id, const String& data);
HttpClient& KeepAlive(bool k) { keepalive = k; return *this; }
@ -60,6 +62,8 @@ public:
bool IsRedirect() const { return is_redirect; }
String GetRedirectURL() const { return redirect_url; }
String CalculateDigest(String authenticate) const;
void Close() { socket.Close(); }
static void Trace(bool b = true);
@ -68,6 +72,7 @@ public:
Socket socket;
bool keepalive;
bool aborted;
bool force_digest;
String error;
int timeout_msecs;
@ -84,6 +89,7 @@ public:
String path;
String username;
String password;
String digest;
String client_headers;
String accept;
String agent;
@ -100,6 +106,7 @@ public:
String server_headers;
String redirect_url;
String authenticate;
enum {
DEFAULT_PORT = 80,

View file

@ -217,6 +217,19 @@ String BinHexEncode(const char *s, const char *e)
return out;
}
String BinhexEncode(const char *s, const char *e)
{
static const char bh[] = "0123456789abcdef";
int l = int(e - s);
StringBuffer out(2 * l);
char *p = out;
for(; s < e; s++) {
*p++ = bh[(*s >> 4) & 0xF];
*p++ = bh[*s & 0xF];
}
return out;
}
String BinHexDecode(const char *p, const char *e)
{
StringBuffer out;

View file

@ -12,6 +12,8 @@ String OtpEncode(String password, String otp_key);
String EncryptString(String password, String otp_key);
String BinHexEncode(const char *b, const char *e);
inline String BinHexEncode(String data) { return BinHexEncode(data.Begin(), data.End()); }
String BinhexEncode(const char *b, const char *e);
inline String BinhexEncode(String data) { return BinhexEncode(data.Begin(), data.End()); }
String BinHexDecode(const char *b, const char *e);
inline String BinHexDecode(String data) { return BinHexDecode(data.Begin(), data.End()); }
String Base64Encode(const char *b, const char *e);