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