Panda3D
Loading...
Searching...
No Matches
httpDigestAuthorization.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file httpDigestAuthorization.cxx
10 * @author drose
11 * @date 2002-10-25
12 */
13
15
16#ifdef HAVE_OPENSSL
17
18#include "httpChannel.h"
19#include "openSSLWrapper.h" // must be included before any other openssl.
20#include <openssl/ssl.h>
21#include <openssl/md5.h>
22#include <time.h>
23
24using std::ostream;
25using std::ostringstream;
26using std::string;
27
28const string HTTPDigestAuthorization::_mechanism = "digest";
29
30/**
31 *
32 */
33HTTPDigestAuthorization::
34HTTPDigestAuthorization(const HTTPAuthorization::Tokens &tokens,
35 const URLSpec &url, bool is_proxy) :
36 HTTPAuthorization(tokens, url, is_proxy)
37{
38 Tokens::const_iterator ti;
39 ti = tokens.find("nonce");
40 if (ti != tokens.end()) {
41 _nonce = (*ti).second;
42 }
43 _nonce_count = 0;
44
45 ti = tokens.find("opaque");
46 if (ti != tokens.end()) {
47 _opaque = (*ti).second;
48 }
49
50 _algorithm = A_md5;
51 ti = tokens.find("algorithm");
52 if (ti != tokens.end()) {
53 string algo_str = HTTPChannel::downcase((*ti).second);
54 if (algo_str == "md5") {
55 _algorithm = A_md5;
56 } else if (algo_str == "md5-sess") {
57 _algorithm = A_md5_sess;
58 } else {
59 _algorithm = A_unknown;
60 }
61 }
62
63 _qop = 0;
64 ti = tokens.find("qop");
65 if (ti != tokens.end()) {
66 string qop_str = HTTPChannel::downcase((*ti).second);
67 // A comma-delimited list of tokens.
68
69 size_t p = 0;
70 while (p < qop_str.length()) {
71 while (p < qop_str.length() && isspace(qop_str[p])) {
72 ++p;
73 }
74 size_t q = p;
75 size_t last_char = p;
76 while (q < qop_str.length() && qop_str[q] != ',') {
77 if (!isspace(qop_str[q])) {
78 qop_str[q] = tolower(qop_str[q]);
79 last_char = q;
80 }
81 ++q;
82 }
83
84 if (last_char > p) {
85 _qop |= match_qop_token(qop_str.substr(p, last_char - p + 1));
86 }
87 p = q + 1;
88 }
89 }
90
91 // Compute an arbitrary client nonce.
92 ostringstream strm;
93 strm << time(nullptr) << ":" << clock() << ":"
94 << url.get_url() << ":Panda";
95
96 _cnonce = calc_md5(strm.str());
97}
98
99/**
100 *
101 */
102HTTPDigestAuthorization::
103~HTTPDigestAuthorization() {
104}
105
106/**
107 * Returns true if the authorization challenge was correctly parsed and is
108 * usable, or false if there was some unsupported algorithm or some such
109 * requested by the server, rendering the challenge unmeetable.
110 */
111bool HTTPDigestAuthorization::
112is_valid() {
113 return (_algorithm != A_unknown);
114}
115
116/**
117 * Returns the type of authorization mechanism, represented as a string, e.g.
118 * "digest".
119 */
120const string &HTTPDigestAuthorization::
121get_mechanism() const {
122 return _mechanism;
123}
124
125/**
126 * Generates a suitable authorization string to send to the server, based on
127 * the data stored within this object, for retrieving the indicated URL with
128 * the given username:password.
129 */
130string HTTPDigestAuthorization::
131generate(HTTPEnum::Method method, const string &request_path,
132 const string &username, const string &body) {
133 _nonce_count++;
134
135 size_t colon = username.find(':');
136 string username_only = username.substr(0, colon);
137 string password_only = username.substr(colon + 1);
138
139 string digest = calc_request_digest(username_only, password_only,
140 method, request_path, body);
141
142 ostringstream strm;
143 strm << "Digest username=\"" << username.substr(0, colon) << "\""
144 << ", realm=\"" << get_realm() << "\""
145 << ", nonce=\"" << _nonce << "\""
146 << ", uri=" << request_path
147 << ", response=\"" << digest << "\""
148 << ", algorithm=" << _algorithm;
149
150 if (!_opaque.empty()) {
151 strm << ", opaque=\"" << _opaque << "\"";
152 }
153
154 if (_chosen_qop != Q_unused) {
155 strm << ", qop=" << _chosen_qop
156 << ", cnonce=\"" << _cnonce << "\""
157 << ", nc=" << get_hex_nonce_count();
158 }
159
160 return strm.str();
161}
162
163/**
164 * Returns the bitfield corresponding to the indicated qop token string, or 0
165 * if the token string is unrecognized.
166 */
167int HTTPDigestAuthorization::
168match_qop_token(const string &token) {
169 if (token == "auth") {
170 return Q_auth;
171 } else if (token == "auth-int") {
172 return Q_auth_int;
173 }
174 return 0;
175}
176
177/**
178 * Calculates the appropriate digest response, according to RFC 2617.
179 */
180string HTTPDigestAuthorization::
181calc_request_digest(const string &username, const string &password,
182 HTTPEnum::Method method, const string &request_path,
183 const string &body) {
184 _chosen_qop = Q_unused;
185 string h_a1 = calc_h(get_a1(username, password));
186 string h_a2 = calc_h(get_a2(method, request_path, body));
187
188 ostringstream strm;
189
190 if (_qop == 0) {
191 _chosen_qop = Q_unused;
192 strm << _nonce << ":" << h_a2;
193
194 } else {
195 strm << _nonce << ":" << get_hex_nonce_count() << ":"
196 << _cnonce << ":" << _chosen_qop << ":"
197 << h_a2;
198 }
199
200 return calc_kd(h_a1, strm.str());
201}
202
203/**
204 * Applies the specified checksum algorithm to the data, according to RFC
205 * 2617.
206 */
207string HTTPDigestAuthorization::
208calc_h(const string &data) const {
209 switch (_algorithm) {
210 case A_unknown:
211 case A_md5:
212 case A_md5_sess:
213 return calc_md5(data);
214 }
215
216 return string();
217}
218
219/**
220 * Applies the specified digest algorithm to the indicated data with the
221 * indicated secret, according to RFC 2617.
222 */
223string HTTPDigestAuthorization::
224calc_kd(const string &secret, const string &data) const {
225 switch (_algorithm) {
226 case A_unknown:
227 case A_md5:
228 case A_md5_sess:
229 return calc_h(secret + ":" + data);
230 }
231
232 return string();
233}
234
235/**
236 * Returns the A1 value, as defined by RFC 2617.
237 */
238string HTTPDigestAuthorization::
239get_a1(const string &username, const string &password) {
240 switch (_algorithm) {
241 case A_unknown:
242 case A_md5:
243 return username + ":" + get_realm() + ":" + password;
244
245 case A_md5_sess:
246 if (_a1.empty()) {
247 _a1 = calc_h(username + ":" + get_realm() + ":" + password) +
248 ":" + _nonce + ":" + _cnonce;
249 }
250 return _a1;
251 }
252
253 return string();
254}
255
256/**
257 * Returns the A2 value, as defined by RFC 2617.
258 */
259string HTTPDigestAuthorization::
260get_a2(HTTPEnum::Method method, const string &request_path,
261 const string &body) {
262 ostringstream strm;
263
264 if ((_qop & Q_auth_int) != 0 && !body.empty()) {
265 _chosen_qop = Q_auth_int;
266 strm << method << ":" << request_path << ":" << calc_h(body);
267
268 } else {
269 _chosen_qop = Q_auth;
270 strm << method << ":" << request_path;
271 }
272
273 return strm.str();
274}
275
276/**
277 * Returns the current nonce count (the number of times we have used the
278 * server's nonce value, including this time) as an eight-digit hexadecimal
279 * value.
280 */
281string HTTPDigestAuthorization::
282get_hex_nonce_count() const {
283 ostringstream strm;
284 strm << std::hex << std::setfill('0') << std::setw(8) << _nonce_count;
285 return strm.str();
286}
287
288/**
289 * Computes the MD5 of the indicated source string and returns it as a
290 * hexadecimal string of 32 ASCII characters.
291 */
292string HTTPDigestAuthorization::
293calc_md5(const string &source) {
294 unsigned char binary[MD5_DIGEST_LENGTH];
295
296 MD5((const unsigned char *)source.data(), source.length(), binary);
297
298 string result;
299 result.reserve(MD5_DIGEST_LENGTH * 2);
300
301 for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
302 result += hexdigit((binary[i] >> 4) & 0xf);
303 result += hexdigit(binary[i] & 0xf);
304 }
305
306 return result;
307}
308
309ostream &
310operator << (ostream &out, HTTPDigestAuthorization::Algorithm algorithm) {
311 switch (algorithm) {
312 case HTTPDigestAuthorization::A_md5:
313 out << "MD5";
314 break;
315 case HTTPDigestAuthorization::A_md5_sess:
316 out << "MD5-sess";
317 break;
318 case HTTPDigestAuthorization::A_unknown:
319 out << "unknown";
320 break;
321 }
322
323 return out;
324}
325
326ostream &
327operator << (ostream &out, HTTPDigestAuthorization::Qop qop) {
328 switch (qop) {
329 case HTTPDigestAuthorization::Q_auth:
330 out << "auth";
331 break;
332 case HTTPDigestAuthorization::Q_auth_int:
333 out << "auth-int";
334 break;
335 case HTTPDigestAuthorization::Q_unused:
336 out << "unused";
337 break;
338 }
339
340 return out;
341}
342
343#endif // HAVE_OPENSSL
A container for a URL, e.g.
Definition urlSpec.h:28
const std::string & get_url() const
Returns the complete URL specification.
Definition urlSpec.I:184
STL class.
STL class.
STL class.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.