375 lines
11 KiB
C++
375 lines
11 KiB
C++
// Filename: httpDigestAuthorization.cxx
|
|
// Created by: drose (25Oct02)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
|
|
//
|
|
// All use of this software is subject to the terms of the Panda 3d
|
|
// Software license. You should have received a copy of this license
|
|
// along with this source code; you will also find a current copy of
|
|
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
|
|
//
|
|
// To contact the maintainers of this program write to
|
|
// panda3d-general@lists.sourceforge.net .
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "httpDigestAuthorization.h"
|
|
|
|
#ifdef HAVE_OPENSSL
|
|
|
|
#include "openssl/ssl.h"
|
|
#include "openssl/md5.h"
|
|
#include <time.h>
|
|
|
|
const string HTTPDigestAuthorization::_mechanism = "digest";
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: HTTPDigestAuthorization::Constructor
|
|
// Access: Protected
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
HTTPDigestAuthorization::
|
|
HTTPDigestAuthorization(const HTTPAuthorization::Tokens &tokens,
|
|
const URLSpec &url, bool is_proxy) :
|
|
HTTPAuthorization(tokens, url, is_proxy)
|
|
{
|
|
Tokens::const_iterator ti;
|
|
ti = tokens.find("nonce");
|
|
if (ti != tokens.end()) {
|
|
_nonce = (*ti).second;
|
|
}
|
|
_nonce_count = 0;
|
|
|
|
ti = tokens.find("opaque");
|
|
if (ti != tokens.end()) {
|
|
_opaque = (*ti).second;
|
|
}
|
|
|
|
_algorithm = A_md5;
|
|
ti = tokens.find("algorithm");
|
|
if (ti != tokens.end()) {
|
|
string algo_str = HTTPChannel::downcase((*ti).second);
|
|
if (algo_str == "md5") {
|
|
_algorithm = A_md5;
|
|
} else if (algo_str == "md5-sess") {
|
|
_algorithm = A_md5_sess;
|
|
} else {
|
|
_algorithm = A_unknown;
|
|
}
|
|
}
|
|
|
|
_qop = 0;
|
|
ti = tokens.find("qop");
|
|
if (ti != tokens.end()) {
|
|
string qop_str = HTTPChannel::downcase((*ti).second);
|
|
// A comma-delimited list of tokens.
|
|
|
|
size_t p = 0;
|
|
while (p < qop_str.length()) {
|
|
while (p < qop_str.length() && isspace(qop_str[p])) {
|
|
++p;
|
|
}
|
|
size_t q = p;
|
|
size_t last_char = p;
|
|
while (q < qop_str.length() && qop_str[q] != ',') {
|
|
if (!isspace(qop_str[q])) {
|
|
qop_str[q] = tolower(qop_str[q]);
|
|
last_char = q;
|
|
}
|
|
++q;
|
|
}
|
|
|
|
if (last_char > p) {
|
|
_qop |= match_qop_token(qop_str.substr(p, last_char - p + 1));
|
|
}
|
|
p = q + 1;
|
|
}
|
|
}
|
|
|
|
// Compute an arbitrary client nonce.
|
|
ostringstream strm;
|
|
strm << time(NULL) << ":" << clock() << ":"
|
|
<< url.get_url() << ":Panda";
|
|
|
|
_cnonce = calc_md5(strm.str());
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: HTTPDigestAuthorization::Destructor
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
HTTPDigestAuthorization::
|
|
~HTTPDigestAuthorization() {
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: HTTPDigestAuthorization::is_valid
|
|
// Access: Public, Virtual
|
|
// Description: Returns true if the authorization challenge was
|
|
// correctly parsed and is usable, or false if there was
|
|
// some unsupported algorithm or some such requested by
|
|
// the server, rendering the challenge unmeetable.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool HTTPDigestAuthorization::
|
|
is_valid() {
|
|
return (_algorithm != A_unknown);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: HTTPDigestAuthorization::get_mechanism
|
|
// Access: Public, Virtual
|
|
// Description: Returns the type of authorization mechanism,
|
|
// represented as a string, e.g. "digest".
|
|
////////////////////////////////////////////////////////////////////
|
|
const string &HTTPDigestAuthorization::
|
|
get_mechanism() const {
|
|
return _mechanism;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: HTTPDigestAuthorization::generate
|
|
// Access: Public, Virtual
|
|
// Description: Generates a suitable authorization string to send
|
|
// to the server, based on the data stored within this
|
|
// object, for retrieving the indicated URL with the
|
|
// given username:password.
|
|
////////////////////////////////////////////////////////////////////
|
|
string HTTPDigestAuthorization::
|
|
generate(HTTPEnum::Method method, const string &request_path,
|
|
const string &username, const string &body) {
|
|
_nonce_count++;
|
|
|
|
size_t colon = username.find(':');
|
|
string username_only = username.substr(0, colon);
|
|
string password_only = username.substr(colon + 1);
|
|
|
|
string digest = calc_request_digest(username_only, password_only,
|
|
method, request_path, body);
|
|
|
|
ostringstream strm;
|
|
strm << "Digest username=\"" << username.substr(0, colon) << "\""
|
|
<< ", realm=\"" << get_realm() << "\""
|
|
<< ", nonce=\"" << _nonce << "\""
|
|
<< ", uri=" << request_path
|
|
<< ", response=\"" << digest << "\""
|
|
<< ", algorithm=" << _algorithm;
|
|
|
|
if (!_opaque.empty()) {
|
|
strm << ", opaque=\"" << _opaque << "\"";
|
|
}
|
|
|
|
if (_chosen_qop != Q_unused) {
|
|
strm << ", qop=" << _chosen_qop
|
|
<< ", cnonce=\"" << _cnonce << "\""
|
|
<< ", nc=" << get_hex_nonce_count();
|
|
}
|
|
|
|
return strm.str();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: HTTPDigestAuthorization::match_qop_token
|
|
// Access: Private, Static
|
|
// Description: Returns the bitfield corresponding to the indicated
|
|
// qop token string, or 0 if the token string is
|
|
// unrecognized.
|
|
////////////////////////////////////////////////////////////////////
|
|
int HTTPDigestAuthorization::
|
|
match_qop_token(const string &token) {
|
|
if (token == "auth") {
|
|
return Q_auth;
|
|
} else if (token == "auth-int") {
|
|
return Q_auth_int;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: HTTPDigestAuthorization::calc_request_digest
|
|
// Access: Private
|
|
// Description: Calculates the appropriate digest response, according
|
|
// to RFC 2617.
|
|
////////////////////////////////////////////////////////////////////
|
|
string HTTPDigestAuthorization::
|
|
calc_request_digest(const string &username, const string &password,
|
|
HTTPEnum::Method method, const string &request_path,
|
|
const string &body) {
|
|
_chosen_qop = Q_unused;
|
|
string h_a1 = calc_h(get_a1(username, password));
|
|
string h_a2 = calc_h(get_a2(method, request_path, body));
|
|
|
|
ostringstream strm;
|
|
|
|
if (_qop == 0) {
|
|
_chosen_qop = Q_unused;
|
|
strm << _nonce << ":" << h_a2;
|
|
|
|
} else {
|
|
strm << _nonce << ":" << get_hex_nonce_count() << ":"
|
|
<< _cnonce << ":" << _chosen_qop << ":"
|
|
<< h_a2;
|
|
}
|
|
|
|
return calc_kd(h_a1, strm.str());
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: HTTPDigestAuthorization::calc_h
|
|
// Access: Private
|
|
// Description: Applies the specified checksum algorithm to the data,
|
|
// according to RFC 2617.
|
|
////////////////////////////////////////////////////////////////////
|
|
string HTTPDigestAuthorization::
|
|
calc_h(const string &data) const {
|
|
switch (_algorithm) {
|
|
case A_unknown:
|
|
case A_md5:
|
|
case A_md5_sess:
|
|
return calc_md5(data);
|
|
}
|
|
|
|
return string();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: HTTPDigestAuthorization::calc_kd
|
|
// Access: Private
|
|
// Description: Applies the specified digest algorithm to the
|
|
// indicated data with the indicated secret, according
|
|
// to RFC 2617.
|
|
////////////////////////////////////////////////////////////////////
|
|
string HTTPDigestAuthorization::
|
|
calc_kd(const string &secret, const string &data) const {
|
|
switch (_algorithm) {
|
|
case A_unknown:
|
|
case A_md5:
|
|
case A_md5_sess:
|
|
return calc_h(secret + ":" + data);
|
|
}
|
|
|
|
return string();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: HTTPDigestAuthorization::get_a1
|
|
// Access: Private
|
|
// Description: Returns the A1 value, as defined by RFC 2617.
|
|
////////////////////////////////////////////////////////////////////
|
|
string HTTPDigestAuthorization::
|
|
get_a1(const string &username, const string &password) {
|
|
switch (_algorithm) {
|
|
case A_unknown:
|
|
case A_md5:
|
|
return username + ":" + get_realm() + ":" + password;
|
|
|
|
case A_md5_sess:
|
|
if (_a1.empty()) {
|
|
_a1 = calc_h(username + ":" + get_realm() + ":" + password) +
|
|
":" + _nonce + ":" + _cnonce;
|
|
}
|
|
return _a1;
|
|
}
|
|
|
|
return string();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: HTTPDigestAuthorization::get_a2
|
|
// Access: Private
|
|
// Description: Returns the A2 value, as defined by RFC 2617.
|
|
////////////////////////////////////////////////////////////////////
|
|
string HTTPDigestAuthorization::
|
|
get_a2(HTTPEnum::Method method, const string &request_path,
|
|
const string &body) {
|
|
ostringstream strm;
|
|
|
|
if ((_qop & Q_auth_int) != 0 && !body.empty()) {
|
|
_chosen_qop = Q_auth_int;
|
|
strm << method << ":" << request_path << ":" << calc_h(body);
|
|
|
|
} else {
|
|
_chosen_qop = Q_auth;
|
|
strm << method << ":" << request_path;
|
|
}
|
|
|
|
return strm.str();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: HTTPDigestAuthorization::get_hex_nonce_count
|
|
// Access: Private
|
|
// Description: Returns the current nonce count (the number of times
|
|
// we have used the server's nonce value, including this
|
|
// time) as an eight-digit hexadecimal value.
|
|
////////////////////////////////////////////////////////////////////
|
|
string HTTPDigestAuthorization::
|
|
get_hex_nonce_count() const {
|
|
ostringstream strm;
|
|
strm << hex << setfill('0') << setw(8) << _nonce_count;
|
|
return strm.str();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: HTTPDigestAuthorization::calc_md5
|
|
// Access: Private, Static
|
|
// Description: Computes the MD5 of the indicated source string and
|
|
// returns it as a hexadecimal string of 32 ASCII
|
|
// characters.
|
|
////////////////////////////////////////////////////////////////////
|
|
string HTTPDigestAuthorization::
|
|
calc_md5(const string &source) {
|
|
unsigned char binary[MD5_DIGEST_LENGTH];
|
|
|
|
MD5((const unsigned char *)source.data(), source.length(), binary);
|
|
|
|
string result;
|
|
result.reserve(MD5_DIGEST_LENGTH * 2);
|
|
|
|
for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
|
|
result += hexdigit((binary[i] >> 4) & 0xf);
|
|
result += hexdigit(binary[i] & 0xf);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
ostream &
|
|
operator << (ostream &out, HTTPDigestAuthorization::Algorithm algorithm) {
|
|
switch (algorithm) {
|
|
case HTTPDigestAuthorization::A_md5:
|
|
out << "MD5";
|
|
break;
|
|
case HTTPDigestAuthorization::A_md5_sess:
|
|
out << "MD5-sess";
|
|
break;
|
|
case HTTPDigestAuthorization::A_unknown:
|
|
out << "unknown";
|
|
break;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
ostream &
|
|
operator << (ostream &out, HTTPDigestAuthorization::Qop qop) {
|
|
switch (qop) {
|
|
case HTTPDigestAuthorization::Q_auth:
|
|
out << "auth";
|
|
break;
|
|
case HTTPDigestAuthorization::Q_auth_int:
|
|
out << "auth-int";
|
|
break;
|
|
case HTTPDigestAuthorization::Q_unused:
|
|
out << "unused";
|
|
break;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
#endif // HAVE_OPENSSL
|