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