Panda3D
httpAuthorization.cxx
1 // Filename: httpAuthorization.cxx
2 // Created by: drose (22Oct02)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "httpAuthorization.h"
16 #include "httpChannel.h"
17 #include "urlSpec.h"
18 
19 #ifdef HAVE_OPENSSL
20 
21 static const char base64_table[64] = {
22  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
23  'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
24  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
25  'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
26  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
27  'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
28  'w', 'x', 'y', 'z', '0', '1', '2', '3',
29  '4', '5', '6', '7', '8', '9', '+', '/',
30 };
31 
32 static unsigned char base64_invert[128];
33 static bool got_base64_invert = false;
34 
35 ////////////////////////////////////////////////////////////////////
36 // Function: HTTPAuthorization::Constructor
37 // Access: Protected
38 // Description:
39 ////////////////////////////////////////////////////////////////////
40 HTTPAuthorization::
41 HTTPAuthorization(const HTTPAuthorization::Tokens &tokens,
42  const URLSpec &url, bool is_proxy) {
43  Tokens::const_iterator ti;
44  ti = tokens.find("realm");
45  if (ti != tokens.end()) {
46  _realm = (*ti).second;
47  }
48 
49  URLSpec canon = get_canonical_url(url);
50 
51  ti = tokens.find("domain");
52  if (ti != tokens.end() && !is_proxy) {
53  // Now the domain consists of a series of space-separated URL
54  // prefixes.
55  const string &domain = (*ti).second;
56  size_t p = 0;
57  while (p < domain.length()) {
58  while (p < domain.length() && isspace(domain[p])) {
59  ++p;
60  }
61  size_t q = p;
62  while (q < domain.length() && !isspace(domain[q])) {
63  ++q;
64  }
65  if (q > p) {
66  string domain_str = domain.substr(p, q - p);
67  URLSpec domain_url(domain_str);
68  if (domain_url.has_server()) {
69  // A fully-qualified URL.
70  _domain.push_back(get_canonical_url(domain_url).get_url());
71  } else {
72  // A relative URL; relative to this path.
73  domain_url = canon;
74  domain_url.set_path(domain_str);
75  _domain.push_back(domain_url.get_url());
76  }
77  }
78  p = q;
79  }
80 
81  } else {
82  // If no domain is defined by the server, use the supplied URL.
83  // Truncate it to the rightmost slash.
84  string canon_str = canon.get_url();
85  size_t slash = canon_str.rfind('/');
86  nassertv(slash != string::npos);
87  _domain.push_back(canon_str.substr(0, slash + 1));
88  }
89 }
90 
91 ////////////////////////////////////////////////////////////////////
92 // Function: HTTPAuthorization::Destructor
93 // Access: Public, Virtual
94 // Description:
95 ////////////////////////////////////////////////////////////////////
96 HTTPAuthorization::
97 ~HTTPAuthorization() {
98 }
99 
100 ////////////////////////////////////////////////////////////////////
101 // Function: HTTPAuthorization::is_valid
102 // Access: Public, Virtual
103 // Description: Returns true if the authorization challenge was
104 // correctly parsed and is usable, or false if there was
105 // some unsupported algorithm or some such requested by
106 // the server, rendering the challenge unmeetable.
107 ////////////////////////////////////////////////////////////////////
108 bool HTTPAuthorization::
109 is_valid() {
110  return true;
111 }
112 
113 ////////////////////////////////////////////////////////////////////
114 // Function: HTTPAuthorization::parse_authentication_schemes
115 // Access: Public, Static
116 // Description: Decodes the text following a WWW-Authenticate: or
117 // Proxy-Authenticate: header field.
118 ////////////////////////////////////////////////////////////////////
119 void HTTPAuthorization::
120 parse_authentication_schemes(HTTPAuthorization::AuthenticationSchemes &schemes,
121  const string &field_value) {
122  // This string will consist of one or more records of the form:
123  //
124  // scheme token=value[,token=value[,...]]
125  //
126  // If there are multiple records, they will be comma-delimited,
127  // which makes parsing just a bit tricky.
128 
129  // Start by skipping initial whitespace.
130  size_t p = 0;
131  while (p < field_value.length() && isspace(field_value[p])) {
132  ++p;
133  }
134 
135  if (p < field_value.length()) {
136  size_t q = p;
137  while (q < field_value.length() && !isspace(field_value[q])) {
138  ++q;
139  }
140  // Here's our first scheme.
141  string scheme = HTTPChannel::downcase(field_value.substr(p, q - p));
142  Tokens *tokens = &(schemes[scheme]);
143 
144  // Now pull off the tokens, one at a time.
145  p = q + 1;
146  while (p < field_value.length()) {
147  q = p;
148  while (q < field_value.length() && field_value[q] != '=' &&
149  field_value[q] != ',' && !isspace(field_value[q])) {
150  ++q;
151  }
152  if (field_value[q] == '=') {
153  // This is a token.
154  string token = HTTPChannel::downcase(field_value.substr(p, q - p));
155  string value;
156  p = scan_quoted_or_unquoted_string(value, field_value, q + 1);
157  (*tokens)[token] = value;
158 
159  // Skip trailing whitespace and extra commas.
160  while (p < field_value.length() &&
161  (field_value[p] == ',' || isspace(field_value[p]))) {
162  ++p;
163  }
164 
165  } else {
166  // This is not a token; it must be the start of a new scheme.
167  scheme = HTTPChannel::downcase(field_value.substr(p, q - p));
168  tokens = &(schemes[scheme]);
169  p = q + 1;
170  }
171  }
172  }
173 }
174 
175 ////////////////////////////////////////////////////////////////////
176 // Function: HTTPAuthorization::get_canonical_url
177 // Access: Public, Static
178 // Description: Returns the "canonical" URL corresponding to this
179 // URL. This is the same URL with an explicit port
180 // indication, an explicit scheme, and a non-empty path,
181 // etc.
182 ////////////////////////////////////////////////////////////////////
183 URLSpec HTTPAuthorization::
184 get_canonical_url(const URLSpec &url) {
185  URLSpec canon = url;
186  canon.set_scheme(canon.get_scheme());
187  canon.set_username(string());
188  canon.set_port(canon.get_port());
189  canon.set_path(canon.get_path());
190 
191  return canon;
192 }
193 
194 ////////////////////////////////////////////////////////////////////
195 // Function: HTTPAuthorization::base64_encode
196 // Access: Public, Static
197 // Description: Returns the input string encoded using base64. No
198 // respect is paid to maintaining a 76-char line length.
199 ////////////////////////////////////////////////////////////////////
200 string HTTPAuthorization::
201 base64_encode(const string &s) {
202  // Collect the string 3 bytes at a time into 24-bit words, then
203  // output each word using 4 bytes.
204  size_t num_words = (s.size() + 2) / 3;
205  string result;
206  result.reserve(num_words * 4);
207  size_t p;
208  for (p = 0; p + 2 < s.size(); p += 3) {
209  unsigned int word =
210  ((unsigned)s[p] << 16) |
211  ((unsigned)s[p + 1] << 8) |
212  ((unsigned)s[p + 2]);
213  result += base64_table[(word >> 18) & 0x3f];
214  result += base64_table[(word >> 12) & 0x3f];
215  result += base64_table[(word >> 6) & 0x3f];
216  result += base64_table[(word) & 0x3f];
217  }
218  // What's left over?
219  if (p < s.size()) {
220  unsigned int word = ((unsigned)s[p] << 16);
221  p++;
222  if (p < s.size()) {
223  word |= ((unsigned)s[p] << 8);
224  p++;
225  nassertr(p == s.size(), result);
226 
227  result += base64_table[(word >> 18) & 0x3f];
228  result += base64_table[(word >> 12) & 0x3f];
229  result += base64_table[(word >> 6) & 0x3f];
230  result += '=';
231  } else {
232  result += base64_table[(word >> 18) & 0x3f];
233  result += base64_table[(word >> 12) & 0x3f];
234  result += '=';
235  result += '=';
236  }
237  }
238 
239  return result;
240 }
241 
242 ////////////////////////////////////////////////////////////////////
243 // Function: HTTPAuthorization::base64_decode
244 // Access: Public, Static
245 // Description: Returns the string decoded from base64.
246 ////////////////////////////////////////////////////////////////////
247 string HTTPAuthorization::
248 base64_decode(const string &s) {
249  // Build up the invert table if this is the first time.
250  if (!got_base64_invert) {
251  int i;
252  for (i = 0; i < 128; i++) {
253  base64_invert[i] = 0xff;
254  }
255 
256  for (int i = 0; i < 64; i++) {
257  base64_invert[(int)base64_table[i]] = i;
258  }
259 
260  base64_invert[(int)'='] = 0;
261 
262  got_base64_invert = true;
263  }
264 
265  // Collect the string 4 bytes at a time; decode this back into a
266  // 24-bit word and output the 3 corresponding bytes.
267  size_t num_words = s.size() / 4;
268  string result;
269  result.reserve(num_words * 3);
270  size_t p;
271  for (p = 0; p < s.size(); p += 4) {
272  unsigned int c0 = base64_invert[s[p] & 0x7f];
273  unsigned int c1 = base64_invert[s[p + 1] & 0x7f];
274  unsigned int c2 = base64_invert[s[p + 2] & 0x7f];
275  unsigned int c3 = base64_invert[s[p + 3] & 0x7f];
276 
277  unsigned int word =
278  (c0 << 18) | (c1 << 12) | (c2 << 6) | c3;
279 
280  result += (char)((word >> 16) & 0xff);
281  if (s[p + 2] != '=') {
282  result += (char)((word >> 8) & 0xff);
283  if (s[p + 3] != '=') {
284  result += (char)(word & 0xff);
285  }
286  }
287  }
288 
289  return result;
290 }
291 
292 ////////////////////////////////////////////////////////////////////
293 // Function: HTTPAuthorization::scan_quoted_or_unquoted_string
294 // Access: Protected, Static
295 // Description: Scans the string source beginning at character
296 // position start, to identify either the
297 // (space-delimited) unquoted string there, or the
298 // (quote-delimited) quoted string. In either case,
299 // fills the string found into result, and returns the
300 // next character position after the string (or after
301 // its closing quote mark).
302 ////////////////////////////////////////////////////////////////////
303 size_t HTTPAuthorization::
304 scan_quoted_or_unquoted_string(string &result, const string &source,
305  size_t start) {
306  result = string();
307 
308  if (start < source.length()) {
309  if (source[start] == '"') {
310  // Quoted string.
311  size_t p = start + 1;
312  while (p < source.length() && source[p] != '"') {
313  if (source[p] == '\\') {
314  // Backslash escapes.
315  ++p;
316  if (p < source.length()) {
317  result += source[p];
318  ++p;
319  }
320  } else {
321  result += source[p];
322  ++p;
323  }
324  }
325  if (p < source.length()) {
326  ++p;
327  }
328  return p;
329  }
330 
331  // Unquoted string.
332  size_t p = start;
333  while (p < source.length() && source[p] != ',' && !isspace(source[p])) {
334  result += source[p];
335  ++p;
336  }
337 
338  return p;
339  }
340 
341  // Empty string.
342  return start;
343 }
344 
345 #endif // HAVE_OPENSSL
A container for a URL, e.g.
Definition: urlSpec.h:29
int get_port() const
Returns the port number specified by the URL, or the default port if not specified.
Definition: urlSpec.cxx:84
string get_path() const
Returns the path specified by the URL, or "/" if no path is specified.
Definition: urlSpec.cxx:153
void set_scheme(const string &scheme)
Replaces the scheme part of the URL specification.
Definition: urlSpec.cxx:183
const string & get_url() const
Returns the complete URL specification.
Definition: urlSpec.I:254
string get_scheme() const
Returns the scheme specified by the URL, or empty string if no scheme is specified.
Definition: urlSpec.cxx:70
void set_path(const string &path)
Replaces the path part of the URL specification.
Definition: urlSpec.cxx:416
void set_port(const string &port)
Replaces the port part of the URL specification.
Definition: urlSpec.cxx:356
void set_username(const string &username)
Replaces the username part of the URL specification.
Definition: urlSpec.cxx:308