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 #if OPENSSL_VERSION_NUMBER < 0x10100000L
59 static HMAC_CTX* HMAC_CTX_new() {
60  HMAC_CTX *ctx = (HMAC_CTX *)OPENSSL_malloc(sizeof(HMAC_CTX));
61  if (ctx) HMAC_CTX_init(ctx);
62  return ctx;
63 }
64 
65 static void HMAC_CTX_free(HMAC_CTX *ctx) {
66  if (ctx) {
67  HMAC_CTX_cleanup(ctx);
68  OPENSSL_free(ctx);
69  }
70 }
71 #endif
72 
73 
74 // GetHost from URL
75 // Parse an URL and extract the host name and port
76 // Return 0 if OK
77 int parseURL(char *url, char *host, int &port, char **path) {
78  // http://x.y.z.w:p/path
79 
80  *path = 0;
81 
82  // look for the second slash
83  char *p = strstr(url, "//");
84  if (!p) return -1;
85 
86 
87  p += 2;
88 
89  // look for the end of the host:port
90  char *p2 = strchr(p, '/');
91  if (!p2) return -1;
92 
93  *path = p2;
94 
95  char buf[256];
96  int l = std::min((int)(p2 - p), (int)sizeof (buf) - 1);
97  strncpy(buf, p, l);
98  buf[l] = '\0';
99 
100  // Now look for :
101  p = strchr(buf, ':');
102  if (p) {
103  int l = std::min((int)(p - buf), (int)sizeof (buf) - 1);
104  strncpy(host, buf, l);
105  host[l] = '\0';
106 
107  port = atoi(p + 1);
108  } else {
109  port = 0;
110 
111 
112  strcpy(host, buf);
113  }
114 
115  return 0;
116 }
117 
118 
119 // Encode an array of bytes to base64
120 
121 void Tobase64(const unsigned char *input, int length, char *out) {
122  BIO *bmem, *b64;
123  BUF_MEM *bptr;
124 
125  if (!out) return;
126 
127  out[0] = '\0';
128 
129  b64 = BIO_new(BIO_f_base64());
130  BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
131  bmem = BIO_new(BIO_s_mem());
132  BIO_push(b64, bmem);
133  BIO_write(b64, input, length);
134 
135  if (BIO_flush(b64) <= 0) {
136  BIO_free_all(b64);
137  return;
138  }
139 
140  BIO_get_mem_ptr(b64, &bptr);
141 
142 
143  memcpy(out, bptr->data, bptr->length);
144  out[bptr->length] = '\0';
145 
146  BIO_free_all(b64);
147 
148  return;
149 }
150 
151 
152 static int
154 {
155  if (isdigit(c)) {
156  return c - '0';
157  } else {
158  c = tolower(c);
159  if (c >= 'a' && c <= 'f') {
160  return c - 'a' + 10;
161  }
162  return -1;
163  }
164 }
165 
166 
167 // Decode a hex digest array to raw bytes.
168 //
169 bool Fromhexdigest(const unsigned char *input, int length, unsigned char *out) {
170  for (int idx=0; idx < length; idx += 2) {
171  int upper = char_to_int(input[idx]);
172  int lower = char_to_int(input[idx+1]);
173  if ((upper < 0) || (lower < 0)) {
174  return false;
175  }
176  out[idx/2] = (upper << 4) + lower;
177  }
178  return true;
179 }
180 
181 
182 // Simple itoa function
183 std::string itos(long i) {
184  char buf[128];
185  sprintf(buf, "%ld", i);
186 
187  return buf;
188 }
189 
190 
191 
192 // Home made implementation of strchrnul
193 char *mystrchrnul(const char *s, int c) {
194  char *ptr = strchr((char *)s, c);
195 
196  if (!ptr)
197  return strchr((char *)s, '\0');
198 
199  return ptr;
200 }
201 
202 
203 
204 // Calculates the opaque arguments hash, needed for a secure redirection
205 //
206 // - hash is a string that will be filled with the hash
207 //
208 // - fn: the original filename that was requested
209 // - dhost: target redirection hostname
210 // - client: address:port of the client
211 // - tim: creation time of the url
212 // - tim_grace: validity time before and after creation time
213 //
214 // Input for the key (simple shared secret)
215 // - key
216 // - key length
217 //
218 
220  char *hash,
221 
222  const char *fn,
223 
224  kXR_int16 request,
225 
226  XrdSecEntity *secent,
227 
228  time_t tim,
229 
230  const char *key) {
231 
232 
233 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
234  EVP_MAC *mac;
235  EVP_MAC_CTX *ctx;
236  size_t len;
237 #else
238  HMAC_CTX *ctx;
239  unsigned int len;
240 #endif
241  unsigned char mdbuf[EVP_MAX_MD_SIZE];
242  char buf[64];
243  struct tm tms;
244 
245 
246  if (!hash) {
247  return;
248  }
249  hash[0] = '\0';
250 
251  if (!key) {
252  return;
253  }
254 
255  if (!fn || !secent) {
256  return;
257  }
258 
259 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
260 
261  mac = EVP_MAC_fetch(0, "sha256", 0);
262  ctx = EVP_MAC_CTX_new(mac);
263 
264  if (!ctx) {
265  return;
266  }
267 
268 
269  EVP_MAC_init(ctx, (const unsigned char *) key, strlen(key), 0);
270 
271 
272  if (fn)
273  EVP_MAC_update(ctx, (const unsigned char *) fn,
274  strlen(fn) + 1);
275 
276  EVP_MAC_update(ctx, (const unsigned char *) &request,
277  sizeof (request));
278 
279  if (secent->name)
280  EVP_MAC_update(ctx, (const unsigned char *) secent->name,
281  strlen(secent->name) + 1);
282 
283  if (secent->vorg)
284  EVP_MAC_update(ctx, (const unsigned char *) secent->vorg,
285  strlen(secent->vorg) + 1);
286 
287  if (secent->host)
288  EVP_MAC_update(ctx, (const unsigned char *) secent->host,
289  strlen(secent->host) + 1);
290 
291  if (secent->moninfo)
292  EVP_MAC_update(ctx, (const unsigned char *) secent->moninfo,
293  strlen(secent->moninfo) + 1);
294 
295  localtime_r(&tim, &tms);
296  strftime(buf, sizeof (buf), "%s", &tms);
297  EVP_MAC_update(ctx, (const unsigned char *) buf,
298  strlen(buf) + 1);
299 
300  EVP_MAC_final(ctx, mdbuf, &len, EVP_MAX_MD_SIZE);
301 
302  EVP_MAC_CTX_free(ctx);
303  EVP_MAC_free(mac);
304 
305 #else
306 
307  ctx = HMAC_CTX_new();
308 
309  if (!ctx) {
310  return;
311  }
312 
313 
314 
315  HMAC_Init_ex(ctx, (const void *) key, strlen(key), EVP_sha256(), 0);
316 
317 
318  if (fn)
319  HMAC_Update(ctx, (const unsigned char *) fn,
320  strlen(fn) + 1);
321 
322  HMAC_Update(ctx, (const unsigned char *) &request,
323  sizeof (request));
324 
325  if (secent->name)
326  HMAC_Update(ctx, (const unsigned char *) secent->name,
327  strlen(secent->name) + 1);
328 
329  if (secent->vorg)
330  HMAC_Update(ctx, (const unsigned char *) secent->vorg,
331  strlen(secent->vorg) + 1);
332 
333  if (secent->host)
334  HMAC_Update(ctx, (const unsigned char *) secent->host,
335  strlen(secent->host) + 1);
336 
337  if (secent->moninfo)
338  HMAC_Update(ctx, (const unsigned char *) secent->moninfo,
339  strlen(secent->moninfo) + 1);
340 
341  localtime_r(&tim, &tms);
342  strftime(buf, sizeof (buf), "%s", &tms);
343  HMAC_Update(ctx, (const unsigned char *) buf,
344  strlen(buf) + 1);
345 
346  HMAC_Final(ctx, mdbuf, &len);
347 
348  HMAC_CTX_free(ctx);
349 
350 #endif
351 
352  Tobase64(mdbuf, len / 2, hash);
353 }
354 
356  const char *h1,
357  const char *h2) {
358 
359  if (h1 == h2) return 0;
360 
361  if (!h1 || !h2)
362  return 1;
363 
364  return strcmp(h1, h2);
365 
366 }
367 
368 // unquote a string and return a new one
369 
370 char *unquote(char *str) {
371  int l = strlen(str);
372  char *r = (char *) malloc(l + 1);
373  r[0] = '\0';
374  int i, j = 0;
375 
376  for (i = 0; i < l; i++) {
377  if (str[i] == '%') {
378  if (i + 3 > l) {
379  r[j] = '\0';
380  return r;
381  }
382  char savec = str[i + 3];
383  str[i + 3] = '\0';
384 
385  r[j] = strtol(str + i + 1, 0, 16);
386  str[i + 3] = savec;
387 
388  i += 2;
389  } else r[j] = str[i];
390 
391  j++;
392  }
393 
394  r[j] = '\0';
395 
396  return r;
397 
398 }
399 
400 // Quote a string and return a new one
401 
402 char *quote(const char *str) {
403  int l = strlen(str);
404  char *r = (char *) malloc(l*3 + 1);
405  r[0] = '\0';
406  int i, j = 0;
407 
408  for (i = 0; i < l; i++) {
409  char c = str[i];
410 
411  switch (c) {
412  case ' ':
413  strcpy(r + j, "%20");
414  j += 3;
415  break;
416  case '[':
417  strcpy(r + j, "%5B");
418  j += 3;
419  break;
420  case ']':
421  strcpy(r + j, "%5D");
422  j += 3;
423  break;
424  case ':':
425  strcpy(r + j, "%3A");
426  j += 3;
427  break;
428  // case '/':
429  // strcpy(r + j, "%2F");
430  // j += 3;
431  // break;
432  case '#':
433  strcpy(r + j, "%23");
434  j += 3;
435  break;
436  case '\n':
437  strcpy(r + j, "%0A");
438  j += 3;
439  break;
440  case '\r':
441  strcpy(r + j, "%0D");
442  j += 3;
443  break;
444  case '=':
445  strcpy(r + j, "%3D");
446  j += 3;
447  break;
448  default:
449  r[j++] = c;
450  }
451  }
452 
453  r[j] = '\0';
454 
455  return r;
456 }
457 
458 
459 // Escape a string and return a new one
460 
461 char *escapeXML(const char *str) {
462  int l = strlen(str);
463  char *r = (char *) malloc(l*6 + 1);
464  r[0] = '\0';
465  int i, j = 0;
466 
467  for (i = 0; i < l; i++) {
468  char c = str[i];
469 
470  switch (c) {
471  case '"':
472  strcpy(r + j, "&quot;");
473  j += 6;
474  break;
475  case '&':
476  strcpy(r + j, "&amp;");
477  j += 5;
478  break;
479  case '<':
480  strcpy(r + j, "&lt;");
481  j += 4;
482  break;
483  case '>':
484  strcpy(r + j, "&gt;");
485  j += 4;
486  break;
487  case '\'':
488  strcpy(r + j, "&apos;");
489  j += 6;
490  break;
491 
492  default:
493  r[j++] = c;
494  }
495  }
496 
497  r[j] = '\0';
498 
499  return r;
500 }
501 
503 
504  int errNo = XProtocol::toErrno(xrdError);
505  return mapErrNoToHttp(errNo);
506 
507 }
508 
509 int mapErrNoToHttp(int errNo) {
510 
511  switch (errNo) {
512 
513  case EACCES:
514  case EROFS:
515  case EPERM:
516  return HTTP_FORBIDDEN;
517 
518  case EAUTH:
519  return HTTP_UNAUTHORIZED;
520 
521  case ENOENT:
522  return HTTP_NOT_FOUND;
523 
524  case EEXIST:
525  case EISDIR:
526  case ENOTDIR:
527  case ENOTEMPTY:
528  return HTTP_CONFLICT;
529 
530  case EXDEV:
532 
533  case ENAMETOOLONG:
534  return HTTP_URI_TOO_LONG;
535 
536  case ELOOP:
537  return HTTP_LOOP_DETECTED;
538 
539  case ENOSPC:
540  case EDQUOT:
542 
543  case EFBIG:
544  return HTTP_PAYLOAD_TOO_LARGE;
545 
546  case EINVAL:
547  case EBADF:
548  case EFAULT:
549  case ENXIO:
550  case ESPIPE:
551  case EOVERFLOW:
552  return HTTP_BAD_REQUEST;
553 
554  case ENOTSUP: // EOPNOTSUPP
555  return HTTP_NOT_IMPLEMENTED;
556 
557  case EBUSY:
558  case EAGAIN:
559  case EINTR:
560  case ENOMEM:
561  case EMFILE:
562  case ENFILE:
563  case ETXTBSY:
565 
566  case ETIMEDOUT:
567  return HTTP_GATEWAY_TIMEOUT;
568 
569  case ECONNREFUSED:
570  case ECONNRESET:
571  case ENETDOWN:
572  case ENETUNREACH:
573  case EHOSTUNREACH:
574  case EPIPE:
575  return HTTP_BAD_GATEWAY;
576 
577  default:
579  }
580 }
581 
582 std::string httpStatusToString(int status) {
583  switch (status) {
584  // 1xx Informational
585  case 100: return "Continue";
586  case 101: return "Switching Protocols";
587  case 102: return "Processing";
588  case 103: return "Early Hints";
589 
590  // 2xx Success
591  case 200: return "OK";
592  case 201: return "Created";
593  case 202: return "Accepted";
594  case 203: return "Non-Authoritative Information";
595  case 204: return "No Content";
596  case 205: return "Reset Content";
597  case 206: return "Partial Content";
598  case 207: return "Multi-Status";
599  case 208: return "Already Reported";
600  case 226: return "IM Used";
601 
602  // 3xx Redirection
603  case 300: return "Multiple Choices";
604  case 301: return "Moved Permanently";
605  case 302: return "Found";
606  case 303: return "See Other";
607  case 304: return "Not Modified";
608  case 305: return "Use Proxy";
609  case 307: return "Temporary Redirect";
610  case 308: return "Permanent Redirect";
611 
612  // 4xx Client Errors
613  case 400: return "Bad Request";
614  case 401: return "Unauthorized";
615  case 402: return "Payment Required";
616  case 403: return "Forbidden";
617  case 404: return "Not Found";
618  case 405: return "Method Not Allowed";
619  case 406: return "Not Acceptable";
620  case 407: return "Proxy Authentication Required";
621  case 408: return "Request Timeout";
622  case 409: return "Conflict";
623  case 410: return "Gone";
624  case 411: return "Length Required";
625  case 412: return "Precondition Failed";
626  case 413: return "Payload Too Large";
627  case 414: return "URI Too Long";
628  case 415: return "Unsupported Media Type";
629  case 416: return "Range Not Satisfiable";
630  case 417: return "Expectation Failed";
631  case 418: return "I'm a teapot";
632  case 421: return "Misdirected Request";
633  case 422: return "Unprocessable Entity";
634  case 423: return "Locked";
635  case 424: return "Failed Dependency";
636  case 425: return "Too Early";
637  case 426: return "Upgrade Required";
638  case 428: return "Precondition Required";
639  case 429: return "Too Many Requests";
640  case 431: return "Request Header Fields Too Large";
641  case 451: return "Unavailable For Legal Reasons";
642 
643  // 5xx Server Errors
644  case 500: return "Internal Server Error";
645  case 501: return "Not Implemented";
646  case 502: return "Bad Gateway";
647  case 503: return "Service Unavailable";
648  case 504: return "Gateway Timeout";
649  case 505: return "HTTP Version Not Supported";
650  case 506: return "Variant Also Negotiates";
651  case 507: return "Insufficient Storage";
652  case 508: return "Loop Detected";
653  case 510: return "Not Extended";
654  case 511: return "Network Authentication Required";
655 
656  default:
657  switch (status) {
658  case 100 ... 199: return "Informational";
659  case 200 ... 299: return "Success";
660  case 300 ... 399: return "Redirection";
661  case 400 ... 499: return "Client Error";
662  case 500 ... 599: return "Server Error";
663  default: return "Unknown";
664  }
665  }
666 }
XErrorCode
Definition: XProtocol.hh:989
#define EAUTH
Definition: XProtocol.hh:1351
short kXR_int16
Definition: XPtypes.hh:66
void BIO_set_flags(BIO *bio, int flags)
int parseURL(char *url, char *host, int &port, char **path)
Definition: XrdHttpUtils.cc:77
std::string itos(long i)
void Tobase64(const unsigned char *input, int length, char *out)
int compareHash(const char *h1, const char *h2)
char * unquote(char *str)
bool Fromhexdigest(const unsigned char *input, int length, unsigned char *out)
int mapXrdErrToHttp(XErrorCode xrdError)
static int char_to_int(int c)
int mapErrNoToHttp(int errNo)
static void HMAC_CTX_free(HMAC_CTX *ctx)
Definition: XrdHttpUtils.cc:65
static HMAC_CTX * HMAC_CTX_new()
Definition: XrdHttpUtils.cc:59
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)
char * quote(const char *str)
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:1411
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