XRootD
XrdHttpUtils.cc
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // This file is part of XrdHTTP: A pragmatic implementation of the
3 // HTTP/WebDAV protocol for the Xrootd framework
4 //
5 // Copyright (c) 2013 by European Organization for Nuclear Research (CERN)
6 // Author: Fabrizio Furano <furano@cern.ch>
7 // File Date: Apr 2013
8 //------------------------------------------------------------------------------
9 // XRootD is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU Lesser General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
13 //
14 // XRootD is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public License
20 // along with XRootD. If not, see <http://www.gnu.org/licenses/>.
21 //------------------------------------------------------------------------------
22 
23 
24 
25 
26 
27 
28 
29 
40 #include "XrdHttpUtils.hh"
41 
42 #include <cstring>
43 #include <openssl/hmac.h>
44 #include <openssl/bio.h>
45 #include <openssl/buffer.h>
46 #include <openssl/err.h>
47 #include <openssl/ssl.h>
48 # include "sys/param.h"
49 
50 #include <pthread.h>
51 #include <memory>
52 #include <vector>
53 #include <algorithm>
54 
55 #include "XrdSec/XrdSecEntity.hh"
56 #include "XrdOuc/XrdOucString.hh"
57 
58 // Encode an array of bytes to base64
59 
60 void Tobase64(const unsigned char *input, int length, char *out) {
61  BIO *bmem, *b64;
62  BUF_MEM *bptr;
63 
64  if (!out) return;
65 
66  out[0] = '\0';
67 
68  b64 = BIO_new(BIO_f_base64());
69  BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
70  bmem = BIO_new(BIO_s_mem());
71  BIO_push(b64, bmem);
72  BIO_write(b64, input, length);
73 
74  if (BIO_flush(b64) <= 0) {
75  BIO_free_all(b64);
76  return;
77  }
78 
79  BIO_get_mem_ptr(b64, &bptr);
80 
81 
82  memcpy(out, bptr->data, bptr->length);
83  out[bptr->length] = '\0';
84 
85  BIO_free_all(b64);
86 
87  return;
88 }
89 
90 void Tobase64(const std::vector<uint8_t> & input, std::string & base64Output) {
91  BIO *bmem, *b64;
92  BUF_MEM *bptr;
93 
94  base64Output.clear();
95 
96  if(input.empty()) {
97  return;
98  }
99 
100  b64 = BIO_new(BIO_f_base64());
101  BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
102  bmem = BIO_new(BIO_s_mem());
103  BIO_push(b64, bmem);
104  BIO_write(b64, input.data(), input.size());
105 
106  if (BIO_flush(b64) <= 0) {
107  BIO_free_all(b64);
108  return;
109  }
110 
111  BIO_get_mem_ptr(b64, &bptr);
112 
113  base64Output.assign(bptr->data,bptr->length);
114 
115  BIO_free_all(b64);
116 }
117 
118 void base64ToBytes(const std::string & base64digest, std::vector<uint8_t> & outputBytes) {
119  outputBytes.clear();
120 
121  if (base64digest.empty()) {
122  return;
123  }
124 
125  BIO *b64 = BIO_new(BIO_f_base64());
126  BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); // match your encoder
127 
128  BIO *bmem = BIO_new_mem_buf(base64digest.data(), static_cast<int>(base64digest.size()));
129  bmem = BIO_push(b64, bmem);
130 
131  // Estimate maximum size (base64 expands data by ~33%)
132  std::vector<uint8_t> buffer(base64digest.size());
133 
134  int decodedLen = BIO_read(bmem, buffer.data(), static_cast<int>(buffer.size()));
135  if (decodedLen > 0) {
136  buffer.resize(decodedLen);
137  outputBytes.swap(buffer);
138  } else {
139  outputBytes.clear(); // decoding failed
140  }
141 
142  BIO_free_all(bmem);
143 }
144 
145 void bytesToHex(const std::vector<uint8_t> & bytes, std::string & output) {
146  static const char* lut = "0123456789abcdef";
147  output.clear();
148  output.reserve(bytes.size() * 2);
149  for (uint8_t b : bytes) {
150  output.push_back(lut[b >> 4]);
151  output.push_back(lut[b & 0x0F]);
152  }
153 }
154 
155 void base64DecodeHex(const std::string & base64, std::string & hexOutput) {
156  std::vector<uint8_t> bytes;
157  base64ToBytes(base64,bytes);
158  bytesToHex(bytes, hexOutput);
159 }
160 
161 
162 static int
163 char_to_int(int ch)
164 {
165  unsigned char c = static_cast<unsigned char>(ch);
166  if (std::isdigit(c)) {
167  return c - '0';
168  } else {
169  c = ::tolower(c);
170  if (c >= 'a' && c <= 'f') {
171  return c - 'a' + 10;
172  }
173  return -1;
174  }
175 }
176 
177 bool Fromhexdigest(const std::string & hex, std::vector<uint8_t> & outputBytes) {
178  if(hex.size() % 2 != 0) {
179  return false;
180  }
181 
182  outputBytes.reserve(hex.size() / 2);
183 
184  for(size_t i = 0; i < hex.size(); i += 2) {
185  int upper = char_to_int(hex[i]);
186  int lower = char_to_int(hex[i + 1]);
187  if (upper < 0 || lower < 0) return false;
188  outputBytes.push_back(static_cast<uint8_t>((upper << 4) + lower));
189  }
190  return true;
191 }
192 
193 
194 // Simple itoa function
195 std::string itos(long i) {
196  char buf[128];
197  sprintf(buf, "%ld", i);
198 
199  return buf;
200 }
201 
202 
203 
204 // Home made implementation of strchrnul
205 char *mystrchrnul(const char *s, int c) {
206  char *ptr = strchr((char *)s, c);
207 
208  if (!ptr)
209  return strchr((char *)s, '\0');
210 
211  return ptr;
212 }
213 
214 
215 
216 // Calculates the opaque arguments hash, needed for a secure redirection
217 //
218 // - hash is a string that will be filled with the hash
219 //
220 // - fn: the original filename that was requested
221 // - dhost: target redirection hostname
222 // - client: address:port of the client
223 // - tim: creation time of the url
224 // - tim_grace: validity time before and after creation time
225 //
226 // Input for the key (simple shared secret)
227 // - key
228 // - key length
229 //
230 
232  char *hash,
233 
234  const char *fn,
235 
236  kXR_int16 request,
237 
238  XrdSecEntity *secent,
239 
240  time_t tim,
241 
242  const char *key) {
243 
244 
245 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
246  EVP_MAC *mac;
247  EVP_MAC_CTX *ctx;
248  size_t len;
249 #else
250  HMAC_CTX *ctx;
251  unsigned int len;
252 #endif
253  unsigned char mdbuf[EVP_MAX_MD_SIZE];
254  char buf[64];
255  struct tm tms;
256 
257 
258  if (!hash) {
259  return;
260  }
261  hash[0] = '\0';
262 
263  if (!key) {
264  return;
265  }
266 
267  if (!fn || !secent) {
268  return;
269  }
270 
271 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
272 
273  if (!(mac = EVP_MAC_fetch(nullptr, "HMAC", nullptr))) {
274  return;
275  }
276 
277  if (!(ctx = EVP_MAC_CTX_new(mac))) {
278  EVP_MAC_free(mac);
279  return;
280  }
281 
282  OSSL_PARAM params[2] = {
283  OSSL_PARAM_construct_utf8_string("digest", (char*)"SHA256", 0),
284  OSSL_PARAM_construct_end()
285  };
286 
287  if (!EVP_MAC_init(ctx, (const unsigned char *) key, strlen(key), params)) {
288  EVP_MAC_CTX_free(ctx);
289  EVP_MAC_free(mac);
290  return;
291  }
292 
293  if (fn)
294  EVP_MAC_update(ctx, (const unsigned char *) fn,
295  strlen(fn) + 1);
296 
297  EVP_MAC_update(ctx, (const unsigned char *) &request,
298  sizeof (request));
299 
300  if (secent->name)
301  EVP_MAC_update(ctx, (const unsigned char *) secent->name,
302  strlen(secent->name) + 1);
303 
304  if (secent->vorg)
305  EVP_MAC_update(ctx, (const unsigned char *) secent->vorg,
306  strlen(secent->vorg) + 1);
307 
308  if (secent->host)
309  EVP_MAC_update(ctx, (const unsigned char *) secent->host,
310  strlen(secent->host) + 1);
311 
312  if (secent->moninfo)
313  EVP_MAC_update(ctx, (const unsigned char *) secent->moninfo,
314  strlen(secent->moninfo) + 1);
315 
316  localtime_r(&tim, &tms);
317  strftime(buf, sizeof (buf), "%s", &tms);
318  EVP_MAC_update(ctx, (const unsigned char *) buf,
319  strlen(buf) + 1);
320 
321  EVP_MAC_final(ctx, mdbuf, &len, EVP_MAX_MD_SIZE);
322 
323  EVP_MAC_CTX_free(ctx);
324  EVP_MAC_free(mac);
325 
326 #else
327 
328  ctx = HMAC_CTX_new();
329 
330  if (!ctx) {
331  return;
332  }
333 
334 
335 
336  HMAC_Init_ex(ctx, (const void *) key, strlen(key), EVP_sha256(), 0);
337 
338 
339  if (fn)
340  HMAC_Update(ctx, (const unsigned char *) fn,
341  strlen(fn) + 1);
342 
343  HMAC_Update(ctx, (const unsigned char *) &request,
344  sizeof (request));
345 
346  if (secent->name)
347  HMAC_Update(ctx, (const unsigned char *) secent->name,
348  strlen(secent->name) + 1);
349 
350  if (secent->vorg)
351  HMAC_Update(ctx, (const unsigned char *) secent->vorg,
352  strlen(secent->vorg) + 1);
353 
354  if (secent->host)
355  HMAC_Update(ctx, (const unsigned char *) secent->host,
356  strlen(secent->host) + 1);
357 
358  if (secent->moninfo)
359  HMAC_Update(ctx, (const unsigned char *) secent->moninfo,
360  strlen(secent->moninfo) + 1);
361 
362  localtime_r(&tim, &tms);
363  strftime(buf, sizeof (buf), "%s", &tms);
364  HMAC_Update(ctx, (const unsigned char *) buf,
365  strlen(buf) + 1);
366 
367  HMAC_Final(ctx, mdbuf, &len);
368 
369  HMAC_CTX_free(ctx);
370 
371 #endif
372 
373  Tobase64(mdbuf, len / 2, hash);
374 }
375 
377  const char *h1,
378  const char *h2) {
379 
380  if (h1 == h2) return 0;
381 
382  if (!h1 || !h2)
383  return 1;
384 
385  return strcmp(h1, h2);
386 
387 }
388 
389 // unquote a string and return a new one
390 
391 char *unquote(char *str) {
392  int l = strlen(str);
393  char *r = (char *) malloc(l + 1);
394  r[0] = '\0';
395  int i, j = 0;
396 
397  for (i = 0; i < l; i++) {
398  if (str[i] == '%') {
399  if (i + 3 > l) {
400  r[j] = '\0';
401  return r;
402  }
403  char savec = str[i + 3];
404  str[i + 3] = '\0';
405 
406  r[j] = strtol(str + i + 1, 0, 16);
407  str[i + 3] = savec;
408 
409  i += 2;
410  } else r[j] = str[i];
411 
412  j++;
413  }
414 
415  r[j] = '\0';
416 
417  return r;
418 
419 }
420 
421 // Quote a string and return a new one
422 
423 char *quote(const char *str) {
424  int l = strlen(str);
425  char *r = (char *) malloc(l*3 + 1);
426  r[0] = '\0';
427  int i, j = 0;
428 
429  for (i = 0; i < l; i++) {
430  char c = str[i];
431 
432  switch (c) {
433  case ' ':
434  strcpy(r + j, "%20");
435  j += 3;
436  break;
437  case '[':
438  strcpy(r + j, "%5B");
439  j += 3;
440  break;
441  case ']':
442  strcpy(r + j, "%5D");
443  j += 3;
444  break;
445  case ':':
446  strcpy(r + j, "%3A");
447  j += 3;
448  break;
449  // case '/':
450  // strcpy(r + j, "%2F");
451  // j += 3;
452  // break;
453  case '#':
454  strcpy(r + j, "%23");
455  j += 3;
456  break;
457  case '\n':
458  strcpy(r + j, "%0A");
459  j += 3;
460  break;
461  case '\r':
462  strcpy(r + j, "%0D");
463  j += 3;
464  break;
465  case '=':
466  strcpy(r + j, "%3D");
467  j += 3;
468  break;
469  default:
470  r[j++] = c;
471  }
472  }
473 
474  r[j] = '\0';
475 
476  return r;
477 }
478 
479 
480 // Escape a string and return a new one
481 
482 char *escapeXML(const char *str) {
483  int l = strlen(str);
484  char *r = (char *) malloc(l*6 + 1);
485  r[0] = '\0';
486  int i, j = 0;
487 
488  for (i = 0; i < l; i++) {
489  char c = str[i];
490 
491  switch (c) {
492  case '"':
493  strcpy(r + j, "&quot;");
494  j += 6;
495  break;
496  case '&':
497  strcpy(r + j, "&amp;");
498  j += 5;
499  break;
500  case '<':
501  strcpy(r + j, "&lt;");
502  j += 4;
503  break;
504  case '>':
505  strcpy(r + j, "&gt;");
506  j += 4;
507  break;
508  case '\'':
509  strcpy(r + j, "&apos;");
510  j += 6;
511  break;
512 
513  default:
514  r[j++] = c;
515  }
516  }
517 
518  r[j] = '\0';
519 
520  return r;
521 }
522 
524 
525  int errNo = XProtocol::toErrno(xrdError);
526  return mapErrNoToHttp(errNo);
527 
528 }
529 
530 int mapErrNoToHttp(int errNo) {
531 
532  switch (errNo) {
533 
534  case EACCES:
535  case EROFS:
536  case EPERM:
537  return HTTP_FORBIDDEN;
538 
539  case EAUTH:
540  return HTTP_UNAUTHORIZED;
541 
542  case ENOENT:
543  return HTTP_NOT_FOUND;
544 
545  case EEXIST:
546  case EISDIR:
547  case ENOTDIR:
548  case ENOTEMPTY:
549  return HTTP_CONFLICT;
550 
551  case EXDEV:
553 
554  case ENAMETOOLONG:
555  return HTTP_URI_TOO_LONG;
556 
557  case ELOOP:
558  return HTTP_LOOP_DETECTED;
559 
560  case ENOSPC:
561  case EDQUOT:
563 
564  case EFBIG:
565  return HTTP_PAYLOAD_TOO_LARGE;
566 
567  case EINVAL:
568  case EBADF:
569  case EFAULT:
570  case ENXIO:
571  case ESPIPE:
572  case EOVERFLOW:
573  return HTTP_BAD_REQUEST;
574 
575  case ENOTSUP: // EOPNOTSUPP
576  return HTTP_NOT_IMPLEMENTED;
577 
578  case EBUSY:
579  case EAGAIN:
580  case EINTR:
581  case ENOMEM:
582  case EMFILE:
583  case ENFILE:
584  case ETXTBSY:
586 
587  case ETIMEDOUT:
588  return HTTP_GATEWAY_TIMEOUT;
589 
590  case ECONNREFUSED:
591  case ECONNRESET:
592  case ENETDOWN:
593  case ENETUNREACH:
594  case EHOSTUNREACH:
595  case EPIPE:
596  return HTTP_BAD_GATEWAY;
597 
598  default:
600  }
601 }
602 
603 std::string httpStatusToString(int status) {
604  switch (status) {
605  // 1xx Informational
606  case 100: return "Continue";
607  case 101: return "Switching Protocols";
608  case 102: return "Processing";
609  case 103: return "Early Hints";
610 
611  // 2xx Success
612  case 200: return "OK";
613  case 201: return "Created";
614  case 202: return "Accepted";
615  case 203: return "Non-Authoritative Information";
616  case 204: return "No Content";
617  case 205: return "Reset Content";
618  case 206: return "Partial Content";
619  case 207: return "Multi-Status";
620  case 208: return "Already Reported";
621  case 226: return "IM Used";
622 
623  // 3xx Redirection
624  case 300: return "Multiple Choices";
625  case 301: return "Moved Permanently";
626  case 302: return "Found";
627  case 303: return "See Other";
628  case 304: return "Not Modified";
629  case 305: return "Use Proxy";
630  case 307: return "Temporary Redirect";
631  case 308: return "Permanent Redirect";
632 
633  // 4xx Client Errors
634  case 400: return "Bad Request";
635  case 401: return "Unauthorized";
636  case 402: return "Payment Required";
637  case 403: return "Forbidden";
638  case 404: return "Not Found";
639  case 405: return "Method Not Allowed";
640  case 406: return "Not Acceptable";
641  case 407: return "Proxy Authentication Required";
642  case 408: return "Request Timeout";
643  case 409: return "Conflict";
644  case 410: return "Gone";
645  case 411: return "Length Required";
646  case 412: return "Precondition Failed";
647  case 413: return "Payload Too Large";
648  case 414: return "URI Too Long";
649  case 415: return "Unsupported Media Type";
650  case 416: return "Range Not Satisfiable";
651  case 417: return "Expectation Failed";
652  case 418: return "I'm a teapot";
653  case 421: return "Misdirected Request";
654  case 422: return "Unprocessable Entity";
655  case 423: return "Locked";
656  case 424: return "Failed Dependency";
657  case 425: return "Too Early";
658  case 426: return "Upgrade Required";
659  case 428: return "Precondition Required";
660  case 429: return "Too Many Requests";
661  case 431: return "Request Header Fields Too Large";
662  case 451: return "Unavailable For Legal Reasons";
663 
664  // 5xx Server Errors
665  case 500: return "Internal Server Error";
666  case 501: return "Not Implemented";
667  case 502: return "Bad Gateway";
668  case 503: return "Service Unavailable";
669  case 504: return "Gateway Timeout";
670  case 505: return "HTTP Version Not Supported";
671  case 506: return "Variant Also Negotiates";
672  case 507: return "Insufficient Storage";
673  case 508: return "Loop Detected";
674  case 510: return "Not Extended";
675  case 511: return "Network Authentication Required";
676 
677  default:
678  switch (status) {
679  case 100 ... 199: return "Informational";
680  case 200 ... 299: return "Success";
681  case 300 ... 399: return "Redirection";
682  case 400 ... 499: return "Client Error";
683  case 500 ... 599: return "Server Error";
684  default: return "Unknown";
685  }
686  }
687 }
XErrorCode
Definition: XProtocol.hh:1031
#define EAUTH
Definition: XProtocol.hh:1393
short kXR_int16
Definition: XPtypes.hh:66
std::string itos(long i)
void Tobase64(const unsigned char *input, int length, char *out)
Definition: XrdHttpUtils.cc:60
int compareHash(const char *h1, const char *h2)
static int char_to_int(int ch)
char * unquote(char *str)
void base64ToBytes(const std::string &base64digest, std::vector< uint8_t > &outputBytes)
int mapXrdErrToHttp(XErrorCode xrdError)
bool Fromhexdigest(const std::string &hex, std::vector< uint8_t > &outputBytes)
int mapErrNoToHttp(int errNo)
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
char * escapeXML(const char *str)
char * mystrchrnul(const char *s, int c)
std::string httpStatusToString(int status)
void base64DecodeHex(const std::string &base64, std::string &hexOutput)
char * quote(const char *str)
void bytesToHex(const std::vector< uint8_t > &bytes, std::string &output)
Utility functions for XrdHTTP.
@ HTTP_INSUFFICIENT_STORAGE
@ HTTP_BAD_REQUEST
Definition: XrdHttpUtils.hh:81
@ HTTP_LOOP_DETECTED
@ HTTP_SERVICE_UNAVAILABLE
@ HTTP_URI_TOO_LONG
Definition: XrdHttpUtils.hh:95
@ HTTP_UNAUTHORIZED
Definition: XrdHttpUtils.hh:82
@ HTTP_NOT_FOUND
Definition: XrdHttpUtils.hh:85
@ HTTP_FORBIDDEN
Definition: XrdHttpUtils.hh:84
@ HTTP_BAD_GATEWAY
@ HTTP_GATEWAY_TIMEOUT
@ HTTP_INTERNAL_SERVER_ERROR
@ HTTP_PAYLOAD_TOO_LARGE
Definition: XrdHttpUtils.hh:94
@ HTTP_NOT_IMPLEMENTED
@ HTTP_UNPROCESSABLE_ENTITY
@ HTTP_CONFLICT
Definition: XrdHttpUtils.hh:90
static int toErrno(int xerr)
Definition: XProtocol.hh:1453
char * vorg
Entity's virtual organization(s)
Definition: XrdSecEntity.hh:71
char * name
Entity's name.
Definition: XrdSecEntity.hh:69
char * moninfo
Information for monitoring.
Definition: XrdSecEntity.hh:76
char * host
Entity's host name dnr dependent.
Definition: XrdSecEntity.hh:70
@ hex
Definition: XrdSysTrace.hh:42