00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "httpDigestAuthorization.h"
00016
00017 #ifdef HAVE_OPENSSL
00018
00019 #include "httpChannel.h"
00020 #include "openSSLWrapper.h"
00021 #include "openssl/ssl.h"
00022 #include "openssl/md5.h"
00023 #include <time.h>
00024
00025 const string HTTPDigestAuthorization::_mechanism = "digest";
00026
00027
00028
00029
00030
00031
00032 HTTPDigestAuthorization::
00033 HTTPDigestAuthorization(const HTTPAuthorization::Tokens &tokens,
00034 const URLSpec &url, bool is_proxy) :
00035 HTTPAuthorization(tokens, url, is_proxy)
00036 {
00037 Tokens::const_iterator ti;
00038 ti = tokens.find("nonce");
00039 if (ti != tokens.end()) {
00040 _nonce = (*ti).second;
00041 }
00042 _nonce_count = 0;
00043
00044 ti = tokens.find("opaque");
00045 if (ti != tokens.end()) {
00046 _opaque = (*ti).second;
00047 }
00048
00049 _algorithm = A_md5;
00050 ti = tokens.find("algorithm");
00051 if (ti != tokens.end()) {
00052 string algo_str = HTTPChannel::downcase((*ti).second);
00053 if (algo_str == "md5") {
00054 _algorithm = A_md5;
00055 } else if (algo_str == "md5-sess") {
00056 _algorithm = A_md5_sess;
00057 } else {
00058 _algorithm = A_unknown;
00059 }
00060 }
00061
00062 _qop = 0;
00063 ti = tokens.find("qop");
00064 if (ti != tokens.end()) {
00065 string qop_str = HTTPChannel::downcase((*ti).second);
00066
00067
00068 size_t p = 0;
00069 while (p < qop_str.length()) {
00070 while (p < qop_str.length() && isspace(qop_str[p])) {
00071 ++p;
00072 }
00073 size_t q = p;
00074 size_t last_char = p;
00075 while (q < qop_str.length() && qop_str[q] != ',') {
00076 if (!isspace(qop_str[q])) {
00077 qop_str[q] = tolower(qop_str[q]);
00078 last_char = q;
00079 }
00080 ++q;
00081 }
00082
00083 if (last_char > p) {
00084 _qop |= match_qop_token(qop_str.substr(p, last_char - p + 1));
00085 }
00086 p = q + 1;
00087 }
00088 }
00089
00090
00091 ostringstream strm;
00092 strm << time(NULL) << ":" << clock() << ":"
00093 << url.get_url() << ":Panda";
00094
00095 _cnonce = calc_md5(strm.str());
00096 }
00097
00098
00099
00100
00101
00102
00103 HTTPDigestAuthorization::
00104 ~HTTPDigestAuthorization() {
00105 }
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115 bool HTTPDigestAuthorization::
00116 is_valid() {
00117 return (_algorithm != A_unknown);
00118 }
00119
00120
00121
00122
00123
00124
00125
00126 const string &HTTPDigestAuthorization::
00127 get_mechanism() const {
00128 return _mechanism;
00129 }
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139 string HTTPDigestAuthorization::
00140 generate(HTTPEnum::Method method, const string &request_path,
00141 const string &username, const string &body) {
00142 _nonce_count++;
00143
00144 size_t colon = username.find(':');
00145 string username_only = username.substr(0, colon);
00146 string password_only = username.substr(colon + 1);
00147
00148 string digest = calc_request_digest(username_only, password_only,
00149 method, request_path, body);
00150
00151 ostringstream strm;
00152 strm << "Digest username=\"" << username.substr(0, colon) << "\""
00153 << ", realm=\"" << get_realm() << "\""
00154 << ", nonce=\"" << _nonce << "\""
00155 << ", uri=" << request_path
00156 << ", response=\"" << digest << "\""
00157 << ", algorithm=" << _algorithm;
00158
00159 if (!_opaque.empty()) {
00160 strm << ", opaque=\"" << _opaque << "\"";
00161 }
00162
00163 if (_chosen_qop != Q_unused) {
00164 strm << ", qop=" << _chosen_qop
00165 << ", cnonce=\"" << _cnonce << "\""
00166 << ", nc=" << get_hex_nonce_count();
00167 }
00168
00169 return strm.str();
00170 }
00171
00172
00173
00174
00175
00176
00177
00178
00179 int HTTPDigestAuthorization::
00180 match_qop_token(const string &token) {
00181 if (token == "auth") {
00182 return Q_auth;
00183 } else if (token == "auth-int") {
00184 return Q_auth_int;
00185 }
00186 return 0;
00187 }
00188
00189
00190
00191
00192
00193
00194
00195 string HTTPDigestAuthorization::
00196 calc_request_digest(const string &username, const string &password,
00197 HTTPEnum::Method method, const string &request_path,
00198 const string &body) {
00199 _chosen_qop = Q_unused;
00200 string h_a1 = calc_h(get_a1(username, password));
00201 string h_a2 = calc_h(get_a2(method, request_path, body));
00202
00203 ostringstream strm;
00204
00205 if (_qop == 0) {
00206 _chosen_qop = Q_unused;
00207 strm << _nonce << ":" << h_a2;
00208
00209 } else {
00210 strm << _nonce << ":" << get_hex_nonce_count() << ":"
00211 << _cnonce << ":" << _chosen_qop << ":"
00212 << h_a2;
00213 }
00214
00215 return calc_kd(h_a1, strm.str());
00216 }
00217
00218
00219
00220
00221
00222
00223
00224 string HTTPDigestAuthorization::
00225 calc_h(const string &data) const {
00226 switch (_algorithm) {
00227 case A_unknown:
00228 case A_md5:
00229 case A_md5_sess:
00230 return calc_md5(data);
00231 }
00232
00233 return string();
00234 }
00235
00236
00237
00238
00239
00240
00241
00242
00243 string HTTPDigestAuthorization::
00244 calc_kd(const string &secret, const string &data) const {
00245 switch (_algorithm) {
00246 case A_unknown:
00247 case A_md5:
00248 case A_md5_sess:
00249 return calc_h(secret + ":" + data);
00250 }
00251
00252 return string();
00253 }
00254
00255
00256
00257
00258
00259
00260 string HTTPDigestAuthorization::
00261 get_a1(const string &username, const string &password) {
00262 switch (_algorithm) {
00263 case A_unknown:
00264 case A_md5:
00265 return username + ":" + get_realm() + ":" + password;
00266
00267 case A_md5_sess:
00268 if (_a1.empty()) {
00269 _a1 = calc_h(username + ":" + get_realm() + ":" + password) +
00270 ":" + _nonce + ":" + _cnonce;
00271 }
00272 return _a1;
00273 }
00274
00275 return string();
00276 }
00277
00278
00279
00280
00281
00282
00283 string HTTPDigestAuthorization::
00284 get_a2(HTTPEnum::Method method, const string &request_path,
00285 const string &body) {
00286 ostringstream strm;
00287
00288 if ((_qop & Q_auth_int) != 0 && !body.empty()) {
00289 _chosen_qop = Q_auth_int;
00290 strm << method << ":" << request_path << ":" << calc_h(body);
00291
00292 } else {
00293 _chosen_qop = Q_auth;
00294 strm << method << ":" << request_path;
00295 }
00296
00297 return strm.str();
00298 }
00299
00300
00301
00302
00303
00304
00305
00306
00307 string HTTPDigestAuthorization::
00308 get_hex_nonce_count() const {
00309 ostringstream strm;
00310 strm << hex << setfill('0') << setw(8) << _nonce_count;
00311 return strm.str();
00312 }
00313
00314
00315
00316
00317
00318
00319
00320
00321 string HTTPDigestAuthorization::
00322 calc_md5(const string &source) {
00323 unsigned char binary[MD5_DIGEST_LENGTH];
00324
00325 MD5((const unsigned char *)source.data(), source.length(), binary);
00326
00327 string result;
00328 result.reserve(MD5_DIGEST_LENGTH * 2);
00329
00330 for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
00331 result += hexdigit((binary[i] >> 4) & 0xf);
00332 result += hexdigit(binary[i] & 0xf);
00333 }
00334
00335 return result;
00336 }
00337
00338 ostream &
00339 operator << (ostream &out, HTTPDigestAuthorization::Algorithm algorithm) {
00340 switch (algorithm) {
00341 case HTTPDigestAuthorization::A_md5:
00342 out << "MD5";
00343 break;
00344 case HTTPDigestAuthorization::A_md5_sess:
00345 out << "MD5-sess";
00346 break;
00347 case HTTPDigestAuthorization::A_unknown:
00348 out << "unknown";
00349 break;
00350 }
00351
00352 return out;
00353 }
00354
00355 ostream &
00356 operator << (ostream &out, HTTPDigestAuthorization::Qop qop) {
00357 switch (qop) {
00358 case HTTPDigestAuthorization::Q_auth:
00359 out << "auth";
00360 break;
00361 case HTTPDigestAuthorization::Q_auth_int:
00362 out << "auth-int";
00363 break;
00364 case HTTPDigestAuthorization::Q_unused:
00365 out << "unused";
00366 break;
00367 }
00368
00369 return out;
00370 }
00371
00372 #endif // HAVE_OPENSSL