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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.