Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/downloader/httpDigestAuthorization.cxx

Go to the documentation of this file.
00001 // Filename: httpDigestAuthorization.cxx
00002 // Created by:  drose (25Oct02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "httpDigestAuthorization.h"
00020 
00021 #ifdef HAVE_SSL
00022 
00023 #include <openssl/ssl.h>
00024 #include <time.h>
00025 
00026 const string HTTPDigestAuthorization::_mechanism = "digest";
00027 
00028 ////////////////////////////////////////////////////////////////////
00029 //     Function: HTTPDigestAuthorization::Constructor
00030 //       Access: Protected
00031 //  Description: 
00032 ////////////////////////////////////////////////////////////////////
00033 HTTPDigestAuthorization::
00034 HTTPDigestAuthorization(const HTTPAuthorization::Tokens &tokens, 
00035                         const URLSpec &url, bool is_proxy) : 
00036   HTTPAuthorization(tokens, url, is_proxy)
00037 {
00038   Tokens::const_iterator ti;
00039   ti = tokens.find("nonce");
00040   if (ti != tokens.end()) {
00041     _nonce = (*ti).second;
00042   }
00043   _nonce_count = 0;
00044 
00045   ti = tokens.find("opaque");
00046   if (ti != tokens.end()) {
00047     _opaque = (*ti).second;
00048   }
00049 
00050   _algorithm = A_md5;
00051   ti = tokens.find("algorithm");
00052   if (ti != tokens.end()) {
00053     string algo_str = HTTPChannel::downcase((*ti).second);
00054     if (algo_str == "md5") {
00055       _algorithm = A_md5;
00056     } else if (algo_str == "md5-sess") {
00057       _algorithm = A_md5_sess;
00058     } else {
00059       _algorithm = A_unknown;
00060     }
00061   }
00062 
00063   _qop = 0;
00064   ti = tokens.find("qop");
00065   if (ti != tokens.end()) {
00066     string qop_str = HTTPChannel::downcase((*ti).second);
00067     // A comma-delimited list of tokens.
00068 
00069     size_t p = 0;
00070     while (p < qop_str.length()) {
00071       while (p < qop_str.length() && isspace(qop_str[p])) {
00072         ++p;
00073       }
00074       size_t q = p;
00075       size_t last_char = p;
00076       while (q < qop_str.length() && qop_str[q] != ',') {
00077         if (!isspace(qop_str[q])) {
00078           qop_str[q] = tolower(qop_str[q]);
00079           last_char = q;
00080         }
00081         ++q;
00082       }
00083 
00084       if (last_char > p) {
00085         _qop |= match_qop_token(qop_str.substr(p, last_char - p + 1));
00086       }
00087       p = q + 1;
00088     }
00089   }
00090 
00091   // Compute an arbitrary client nonce.
00092   ostringstream strm;
00093   strm << time(NULL) << ":" << clock() << ":" 
00094        << url.get_url() << ":Panda";
00095 
00096   _cnonce = calc_md5(strm.str());
00097 }
00098 
00099 ////////////////////////////////////////////////////////////////////
00100 //     Function: HTTPDigestAuthorization::Destructor
00101 //       Access: Public, Virtual
00102 //  Description: 
00103 ////////////////////////////////////////////////////////////////////
00104 HTTPDigestAuthorization::
00105 ~HTTPDigestAuthorization() {
00106 }
00107 
00108 ////////////////////////////////////////////////////////////////////
00109 //     Function: HTTPDigestAuthorization::is_valid
00110 //       Access: Public, Virtual
00111 //  Description: Returns true if the authorization challenge was
00112 //               correctly parsed and is usable, or false if there was
00113 //               some unsupported algorithm or some such requested by
00114 //               the server, rendering the challenge unmeetable.
00115 ////////////////////////////////////////////////////////////////////
00116 bool HTTPDigestAuthorization::
00117 is_valid() {
00118   return (_algorithm != A_unknown);
00119 }
00120 
00121 ////////////////////////////////////////////////////////////////////
00122 //     Function: HTTPDigestAuthorization::get_mechanism
00123 //       Access: Public, Virtual
00124 //  Description: Returns the type of authorization mechanism,
00125 //               represented as a string, e.g. "digest".
00126 ////////////////////////////////////////////////////////////////////
00127 const string &HTTPDigestAuthorization::
00128 get_mechanism() const {
00129   return _mechanism;
00130 }
00131 
00132 ////////////////////////////////////////////////////////////////////
00133 //     Function: HTTPDigestAuthorization::generate
00134 //       Access: Public, Virtual
00135 //  Description: Generates a suitable authorization string to send
00136 //               to the server, based on the data stored within this
00137 //               object, for retrieving the indicated URL with the
00138 //               given username:password.
00139 ////////////////////////////////////////////////////////////////////
00140 string HTTPDigestAuthorization::
00141 generate(HTTPEnum::Method method, const string &request_path,
00142          const string &username, const string &body) {
00143   _nonce_count++;
00144 
00145   size_t colon = username.find(':');
00146   string username_only = username.substr(0, colon);
00147   string password_only = username.substr(colon + 1);
00148 
00149   string digest = calc_request_digest(username_only, password_only,
00150                                       method, request_path, body);
00151 
00152   ostringstream strm;
00153   strm << "Digest username=\"" << username.substr(0, colon) << "\""
00154        << ", realm=\"" << get_realm() << "\""
00155        << ", nonce=\"" << _nonce << "\""
00156        << ", uri=" << request_path
00157        << ", response=\"" << digest << "\""
00158        << ", algorithm=" << _algorithm;
00159 
00160   if (!_opaque.empty()) {
00161     strm << ", opaque=\"" << _opaque << "\"";
00162   }
00163 
00164   if (_chosen_qop != Q_unused) {
00165     strm << ", qop=" << _chosen_qop
00166          << ", cnonce=\"" << _cnonce << "\""
00167          << ", nc=" << get_hex_nonce_count();
00168   }
00169 
00170   return strm.str();
00171 }
00172 
00173 ////////////////////////////////////////////////////////////////////
00174 //     Function: HTTPDigestAuthorization::match_qop_token
00175 //       Access: Private, Static
00176 //  Description: Returns the bitfield corresponding to the indicated
00177 //               qop token string, or 0 if the token string is
00178 //               unrecognized.
00179 ////////////////////////////////////////////////////////////////////
00180 int HTTPDigestAuthorization::
00181 match_qop_token(const string &token) {
00182   if (token == "auth") {
00183     return Q_auth;
00184   } else if (token == "auth-int") {
00185     return Q_auth_int;
00186   }
00187   return 0;
00188 }
00189 
00190 ////////////////////////////////////////////////////////////////////
00191 //     Function: HTTPDigestAuthorization::calc_request_digest
00192 //       Access: Private
00193 //  Description: Calculates the appropriate digest response, according
00194 //               to RFC 2617.
00195 ////////////////////////////////////////////////////////////////////
00196 string HTTPDigestAuthorization::
00197 calc_request_digest(const string &username, const string &password,
00198                     HTTPEnum::Method method, const string &request_path, 
00199                     const string &body) {
00200   _chosen_qop = Q_unused;
00201   string h_a1 = calc_h(get_a1(username, password));
00202   string h_a2 = calc_h(get_a2(method, request_path, body));
00203 
00204   ostringstream strm;
00205 
00206   if (_qop == 0) {
00207     _chosen_qop = Q_unused;
00208     strm << _nonce << ":" << h_a2;
00209 
00210   } else {
00211     strm << _nonce << ":" << get_hex_nonce_count() << ":"
00212          << _cnonce << ":" << _chosen_qop << ":"
00213          << h_a2;
00214   }
00215 
00216   return calc_kd(h_a1, strm.str());
00217 }
00218 
00219 ////////////////////////////////////////////////////////////////////
00220 //     Function: HTTPDigestAuthorization::calc_h
00221 //       Access: Private
00222 //  Description: Applies the specified checksum algorithm to the data,
00223 //               according to RFC 2617.
00224 ////////////////////////////////////////////////////////////////////
00225 string HTTPDigestAuthorization::
00226 calc_h(const string &data) const {
00227   switch (_algorithm) {
00228   case A_unknown:
00229   case A_md5:
00230   case A_md5_sess:
00231     return calc_md5(data);
00232   }
00233 
00234   return string();
00235 }
00236 
00237 ////////////////////////////////////////////////////////////////////
00238 //     Function: HTTPDigestAuthorization::calc_kd
00239 //       Access: Private
00240 //  Description: Applies the specified digest algorithm to the
00241 //               indicated data with the indicated secret, according
00242 //               to RFC 2617.
00243 ////////////////////////////////////////////////////////////////////
00244 string HTTPDigestAuthorization::
00245 calc_kd(const string &secret, const string &data) const {
00246   switch (_algorithm) {
00247   case A_unknown:
00248   case A_md5:
00249   case A_md5_sess:
00250     return calc_h(secret + ":" + data);
00251   }
00252 
00253   return string();
00254 }
00255 
00256 ////////////////////////////////////////////////////////////////////
00257 //     Function: HTTPDigestAuthorization::get_a1
00258 //       Access: Private
00259 //  Description: Returns the A1 value, as defined by RFC 2617.
00260 ////////////////////////////////////////////////////////////////////
00261 string HTTPDigestAuthorization::
00262 get_a1(const string &username, const string &password) {
00263   switch (_algorithm) {
00264   case A_unknown:
00265   case A_md5:
00266     return username + ":" + get_realm() + ":" + password;
00267 
00268   case A_md5_sess:
00269     if (_a1.empty()) {
00270       _a1 = calc_h(username + ":" + get_realm() + ":" + password) +
00271         ":" + _nonce + ":" + _cnonce;
00272     }
00273     return _a1;
00274   }
00275 
00276   return string();
00277 }
00278 
00279 ////////////////////////////////////////////////////////////////////
00280 //     Function: HTTPDigestAuthorization::get_a2
00281 //       Access: Private
00282 //  Description: Returns the A2 value, as defined by RFC 2617.
00283 ////////////////////////////////////////////////////////////////////
00284 string HTTPDigestAuthorization::
00285 get_a2(HTTPEnum::Method method, const string &request_path,
00286        const string &body) {
00287   ostringstream strm;
00288 
00289   if ((_qop & Q_auth_int) != 0 && !body.empty()) {
00290     _chosen_qop = Q_auth_int;
00291     strm << method << ":" << request_path << ":" << calc_h(body);
00292 
00293   } else {
00294     _chosen_qop = Q_auth;
00295     strm << method << ":" << request_path;
00296   }
00297 
00298   return strm.str();
00299 }
00300 
00301 ////////////////////////////////////////////////////////////////////
00302 //     Function: HTTPDigestAuthorization::get_hex_nonce_count
00303 //       Access: Private
00304 //  Description: Returns the current nonce count (the number of times
00305 //               we have used the server's nonce value, including this
00306 //               time) as an eight-digit hexadecimal value.
00307 ////////////////////////////////////////////////////////////////////
00308 string HTTPDigestAuthorization::
00309 get_hex_nonce_count() const {
00310   ostringstream strm;
00311   strm << hex << setfill('0') << setw(8) << _nonce_count;
00312   return strm.str();
00313 }
00314 
00315 ////////////////////////////////////////////////////////////////////
00316 //     Function: HTTPDigestAuthorization::calc_md5
00317 //       Access: Private, Static
00318 //  Description: Computes the MD5 of the indicated source string and
00319 //               returns it as a hexadecimal string of 32 ASCII
00320 //               characters.
00321 ////////////////////////////////////////////////////////////////////
00322 string HTTPDigestAuthorization::
00323 calc_md5(const string &source) {
00324   unsigned char binary[MD5_DIGEST_LENGTH];
00325 
00326   MD5((const unsigned char *)source.data(), source.length(), binary);
00327 
00328   string result;
00329   result.reserve(MD5_DIGEST_LENGTH * 2);
00330 
00331   for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
00332     result += hexdigit((binary[i] >> 4) & 0xf);
00333     result += hexdigit(binary[i] & 0xf);
00334   }
00335 
00336   return result;
00337 }
00338 
00339 ostream &
00340 operator << (ostream &out, HTTPDigestAuthorization::Algorithm algorithm) {
00341   switch (algorithm) {
00342   case HTTPDigestAuthorization::A_md5:
00343     out << "MD5";
00344     break;
00345   case HTTPDigestAuthorization::A_md5_sess:
00346     out << "MD5-sess";
00347     break;
00348   case HTTPDigestAuthorization::A_unknown:
00349     out << "unknown";
00350     break;
00351   }
00352 
00353   return out;
00354 }
00355 
00356 ostream &
00357 operator << (ostream &out, HTTPDigestAuthorization::Qop qop) {
00358   switch (qop) {
00359   case HTTPDigestAuthorization::Q_auth:
00360     out << "auth";
00361     break;
00362   case HTTPDigestAuthorization::Q_auth_int:
00363     out << "auth-int";
00364     break;
00365   case HTTPDigestAuthorization::Q_unused:
00366     out << "unused";
00367     break;
00368   }
00369 
00370   return out;
00371 }
00372 
00373 #endif  // HAVE_SSL

Generated on Fri May 2 00:36:50 2003 for Panda by doxygen1.3