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# include "sys/param.h"
49
50#include <pthread.h>
51#include <memory>
52#include <vector>
53#include <algorithm>
54
57
58#if OPENSSL_VERSION_NUMBER < 0x10100000L
59static 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
65static 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
77int 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
121void 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
152static 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//
169bool 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
183std::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
193char *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
370char *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
402char *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
461char *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
509int 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:
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
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:
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
582std::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
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)
int mapXrdErrToHttp(XErrorCode xrdError)
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
static int toErrno(int xerr)
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.