"Fossies" - the Fresh Open Source Software Archive

Member "monit-5.28.0/src/http/processor.c" (28 Mar 2021, 33009 Bytes) of package /linux/privat/monit-5.28.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "processor.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.27.2_vs_5.28.0.

    1 /*
    2  * Copyright (C) Tildeslash Ltd. All rights reserved.
    3  *
    4  * This program is free software: you can redistribute it and/or modify
    5  * it under the terms of the GNU Affero General Public License version 3.
    6  *
    7  * This program is distributed in the hope that it will be useful,
    8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   10  * GNU General Public License for more details.
   11  *
   12  * You should have received a copy of the GNU Affero General Public License
   13  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   14  *
   15  * In addition, as a special exception, the copyright holders give
   16  * permission to link the code of portions of this program with the
   17  * OpenSSL library under certain conditions as described in each
   18  * individual source file, and distribute linked combinations
   19  * including the two.
   20  *
   21  * You must obey the GNU Affero General Public License in all respects
   22  * for all of the code used other than OpenSSL.
   23  */
   24 
   25 #include "config.h"
   26 
   27 #ifdef HAVE_STDIO_H
   28 #include <stdio.h>
   29 #endif
   30 
   31 #ifdef HAVE_STDLIB_H
   32 #include <stdlib.h>
   33 #endif
   34 
   35 #ifdef HAVE_ERRNO_H
   36 #include <errno.h>
   37 #endif
   38 
   39 #ifdef HAVE_STDARG_H
   40 #include <stdarg.h>
   41 #endif
   42 
   43 #ifdef HAVE_SYS_TYPES_H
   44 #include <sys/types.h>
   45 #endif
   46 
   47 #ifdef HAVE_SYS_SOCKET_H
   48 #include <sys/socket.h>
   49 #endif
   50 
   51 #ifdef HAVE_SETJMP_H
   52 #include <setjmp.h>
   53 #endif
   54 
   55 #ifdef HAVE_STRING_H
   56 #include <string.h>
   57 #endif
   58 
   59 #ifdef HAVE_STRINGS_H
   60 #include <strings.h>
   61 #endif
   62 
   63 #ifdef HAVE_UNISTD_H
   64 #include <unistd.h>
   65 #endif
   66 
   67 #ifdef HAVE_LIMITS_H
   68 #include <limits.h>
   69 #endif
   70 
   71 #include "monit.h"
   72 #include "processor.h"
   73 #include "base64.h"
   74 
   75 // libmonit
   76 #include "util/Str.h"
   77 #include "system/Net.h"
   78 
   79 
   80 /**
   81  *  A naive quasi HTTP Processor module that can handle HTTP requests
   82  *  received from a client, and return responses based on those
   83  *  requests.
   84  *
   85  *  This Processor delegates the actual handling of the request and
   86  *  response to so called cervlets, which must implement two methods;
   87  *  doGet and doPost.
   88  *
   89  *  NOTES
   90  *    This Processor is command oriented and if a second slash '/' is
   91  *    found in the URL it's assumed to be the PATHINFO. In other words
   92  *    this processor perceive an URL as:
   93  *
   94  *                      /COMMAND?QUERYSTRING/PATHINFO
   95  *
   96  *     The doGet/doPost routines act's on the COMMAND. See the
   97  *     cervlet.c code in this dir. for an example.
   98  *
   99  *  @file
  100  */
  101 
  102 
  103 static int _httpPostLimit;
  104 
  105 
  106 /* -------------------------------------------------------------- Prototypes */
  107 
  108 
  109 static void do_service(Socket_T);
  110 static void destroy_entry(void *);
  111 static char *get_date(char *, int);
  112 static char *get_server(char *, int);
  113 static void create_headers(HttpRequest);
  114 static void send_response(HttpRequest, HttpResponse);
  115 static bool basic_authenticate(HttpRequest);
  116 static void done(HttpRequest, HttpResponse);
  117 static void destroy_HttpRequest(HttpRequest);
  118 static void reset_response(HttpResponse res);
  119 static HttpParameter parse_parameters(char *);
  120 static bool create_parameters(HttpRequest req);
  121 static void destroy_HttpResponse(HttpResponse);
  122 static HttpRequest create_HttpRequest(Socket_T);
  123 static void internal_error(Socket_T, int, const char *);
  124 static HttpResponse create_HttpResponse(Socket_T);
  125 static bool is_authenticated(HttpRequest, HttpResponse);
  126 static int get_next_token(char *s, int *cursor, char **r);
  127 
  128 
  129 /*
  130  * An object for implementors of the service functions; doGet and
  131  * doPost. Implementing modules i.e. CERVLETS, must implement the
  132  * doGet and doPost functions and the engine will call the add_Impl
  133  * function to setup the callback to these functions.
  134  */
  135 struct  ServiceImpl {
  136         void(*doGet)(HttpRequest, HttpResponse);
  137         void(*doPost)(HttpRequest, HttpResponse);
  138 } Impl;
  139 
  140 
  141 /* ------------------------------------------------------------------ Public */
  142 
  143 
  144 /**
  145  * Process a HTTP request. This is done by dispatching to the service
  146  * function.
  147  * @param s A Socket_T representing the client connection
  148  */
  149 void *http_processor(Socket_T s) {
  150         if (! Net_canRead(Socket_getSocket(s), REQUEST_TIMEOUT * 1000))
  151                 internal_error(s, SC_REQUEST_TIMEOUT, "Time out when handling the Request");
  152         else
  153                 do_service(s);
  154         Socket_free(&s);
  155         return NULL;
  156 }
  157 
  158 
  159 /**
  160  * Callback for implementors of cervlet functions.
  161  * @param doGetFunc doGet function
  162  * @param doPostFunc doPost function
  163  */
  164 void add_Impl(void(*doGet)(HttpRequest, HttpResponse), void(*doPost)(HttpRequest, HttpResponse)) {
  165         Impl.doGet = doGet;
  166         Impl.doPost = doPost;
  167 }
  168 
  169 
  170 void Processor_setHttpPostLimit() {
  171         // Base buffer size (space for e.g. "action=<name>")
  172         _httpPostLimit = STRLEN;
  173         // Add space for each service
  174         for (Service_T s = servicelist; s; s = s->next)
  175                 _httpPostLimit += strlen("&service=") + strlen(s->name);
  176 }
  177 
  178 
  179 StringBuffer_T escapeHTML(StringBuffer_T sb, const char *s) {
  180         for (int i = 0; s[i]; i++) {
  181                 if (s[i] == '<')
  182                         StringBuffer_append(sb, "&lt;");
  183                 else if (s[i] == '>')
  184                         StringBuffer_append(sb, "&gt;");
  185                 else if (s[i] == '&')
  186                         StringBuffer_append(sb, "&amp;");
  187                 else
  188                         StringBuffer_append(sb, "%c", s[i]);
  189         }
  190         return sb;
  191 }
  192 
  193 
  194 /**
  195  * Send an error message
  196  * @param res HttpResponse object
  197  * @param code Error Code to lookup and send
  198  * @param msg Optional error message (may be NULL)
  199  */
  200 void send_error(HttpRequest req, HttpResponse res, int code, const char *msg, ...) {
  201         ASSERT(msg);
  202 
  203         const char *err = get_status_string(code);
  204         reset_response(res);
  205         set_content_type(res, "text/html");
  206         set_status(res, code);
  207         StringBuffer_append(res->outputbuffer,
  208                             "<html>"
  209                             "<head>"
  210                             "<title>%d %s</title>"
  211                             "</head>"
  212                             "<body bgcolor=#FFFFFF>"
  213                             "<h2>%s</h2>",
  214                             code, err, err);
  215         char *message;
  216         va_list ap;
  217         va_start(ap, msg);
  218         message = Str_vcat(msg, ap);
  219         va_end(ap);
  220         escapeHTML(res->outputbuffer, message);
  221         if (code != SC_UNAUTHORIZED) // We log details in basic_authenticate() already, no need to log generic error sent to client here
  222                 Log_error("HttpRequest: error -- client [%s]: %s %d %s\n", NVLSTR(Socket_getRemoteHost(req->S)), SERVER_PROTOCOL, code, message);
  223         FREE(message);
  224         char server[STRLEN];
  225         StringBuffer_append(res->outputbuffer,
  226                             "<hr>"
  227                             "<a href='%s'><font size=-1>%s</font></a>"
  228                             "</body>"
  229                             "</html>"
  230                             "\r\n",
  231                             SERVER_URL, get_server(server, STRLEN));
  232 }
  233 
  234 
  235 /* -------------------------------------------------------------- Properties */
  236 
  237 
  238 /**
  239  * Adds a response header with the given name and value. If the header
  240  * had already been set the new value overwrites the previous one.
  241  * @param res HttpResponse object
  242  * @param name Header key name
  243  * @param value Header key value
  244  */
  245 void set_header(HttpResponse res, const char *name, const char *value, ...) {
  246         HttpHeader h = NULL;
  247 
  248         ASSERT(res);
  249         ASSERT(name);
  250 
  251         NEW(h);
  252         h->name = Str_dup(name);
  253         va_list ap;
  254         va_start(ap, value);
  255         h->value = Str_vcat(value, ap);
  256         va_end(ap);
  257         if (res->headers) {
  258                 HttpHeader n, p;
  259                 for (n = p = res->headers; p; n = p, p = p->next) {
  260                         if (IS(p->name, name)) {
  261                                 FREE(p->value);
  262                                 p->value = Str_dup(h->value);
  263                                 destroy_entry(h);
  264                                 return;
  265                         }
  266                 }
  267                 n->next = h;
  268         } else {
  269                 res->headers = h;
  270         }
  271 }
  272 
  273 
  274 /**
  275  * Sets the status code for the response
  276  * @param res HttpResponse object
  277  * @param code A HTTP status code <100-510>
  278  * @param msg The status code string message
  279  */
  280 void set_status(HttpResponse res, int code) {
  281         res->status = code;
  282         res->status_msg = get_status_string(code);
  283 }
  284 
  285 
  286 /**
  287  * Set the response content-type
  288  * @param res HttpResponse object
  289  * @param mime Mime content type, e.g. text/html
  290  */
  291 void set_content_type(HttpResponse res, const char *mime) {
  292         ASSERT(mime);
  293         set_header(res, "Content-Type", "%s", mime);
  294 }
  295 
  296 
  297 /**
  298  * Returns the value of the specified header
  299  * @param req HttpRequest object
  300  * @param name Header name to lookup the value for
  301  * @return The value of the specified header, NULL if not found
  302  */
  303 const char *get_header(HttpRequest req, const char *name) {
  304         for (HttpHeader p = req->headers; p; p = p->next)
  305                 if (IS(p->name, name))
  306                         return (p->value);
  307         return NULL;
  308 }
  309 
  310 
  311 /**
  312  * Returns the value of the specified parameter
  313  * @param req HttpRequest object
  314  * @param name The request parameter key to lookup the value for
  315  * @return The value of the specified parameter, or NULL if not found
  316  */
  317 const char *get_parameter(HttpRequest req, const char *name) {
  318         for (HttpParameter p = req->params; p; p = p->next)
  319                 if (IS(p->name, name))
  320                         return (p->value);
  321         return NULL;
  322 }
  323 
  324 
  325 /**
  326  * Returns a string containing all (extra) headers found in the
  327  * response.  The headers are newline separated in the returned
  328  * string.
  329  * @param res HttpResponse object
  330  * @return A String containing all headers set in the Response object
  331  */
  332 char *get_headers(HttpResponse res) {
  333         char buf[RES_STRLEN];
  334         char *b = buf;
  335         *buf = 0;
  336         for (HttpHeader p = res->headers; (((b - buf) + STRLEN) < RES_STRLEN) && p; p = p->next)
  337                 b += snprintf(b, STRLEN,"%s: %s\r\n", p->name, p->value);
  338         return buf[0] ? Str_dup(buf) : NULL;
  339 }
  340 
  341 
  342 /**
  343  * Lookup the corresponding HTTP status string for the given status
  344  * code
  345  * @param status A HTTP status code
  346  * @return A default status message for the specified HTTP status
  347  * code.
  348  */
  349 const char *get_status_string(int status) {
  350         switch (status) {
  351                 case SC_OK:
  352                         return "OK";
  353                 case SC_ACCEPTED:
  354                         return "Accepted";
  355                 case SC_BAD_GATEWAY:
  356                         return "Bad Gateway";
  357                 case SC_BAD_REQUEST:
  358                         return "Bad Request";
  359                 case SC_CONFLICT:
  360                         return "Conflict";
  361                 case SC_CONTINUE:
  362                         return "Continue";
  363                 case SC_CREATED:
  364                         return "Created";
  365                 case SC_EXPECTATION_FAILED:
  366                         return "Expectation Failed";
  367                 case SC_FORBIDDEN:
  368                         return "Forbidden";
  369                 case SC_GATEWAY_TIMEOUT:
  370                         return "Gateway Timeout";
  371                 case SC_GONE:
  372                         return "Gone";
  373                 case SC_VERSION_NOT_SUPPORTED:
  374                         return "HTTP Version Not Supported";
  375                 case SC_INTERNAL_SERVER_ERROR:
  376                         return "Internal Server Error";
  377                 case SC_LENGTH_REQUIRED:
  378                         return "Length Required";
  379                 case SC_METHOD_NOT_ALLOWED:
  380                         return "Method Not Allowed";
  381                 case SC_MOVED_PERMANENTLY:
  382                         return "Moved Permanently";
  383                 case SC_MOVED_TEMPORARILY:
  384                         return "Moved Temporarily";
  385                 case SC_MULTIPLE_CHOICES:
  386                         return "Multiple Choices";
  387                 case SC_NO_CONTENT:
  388                         return "No Content";
  389                 case SC_NON_AUTHORITATIVE:
  390                         return "Non-Authoritative Information";
  391                 case SC_NOT_ACCEPTABLE:
  392                         return "Not Acceptable";
  393                 case SC_NOT_FOUND:
  394                         return "Not Found";
  395                 case SC_NOT_IMPLEMENTED:
  396                         return "Not Implemented";
  397                 case SC_NOT_MODIFIED:
  398                         return "Not Modified";
  399                 case SC_PARTIAL_CONTENT:
  400                         return "Partial Content";
  401                 case SC_PAYMENT_REQUIRED:
  402                         return "Payment Required";
  403                 case SC_PRECONDITION_FAILED:
  404                         return "Precondition Failed";
  405                 case SC_PROXY_AUTHENTICATION_REQUIRED:
  406                         return "Proxy Authentication Required";
  407                 case SC_REQUEST_ENTITY_TOO_LARGE:
  408                         return "Request Entity Too Large";
  409                 case SC_REQUEST_TIMEOUT:
  410                         return "Request Timeout";
  411                 case SC_REQUEST_URI_TOO_LARGE:
  412                         return "Request URI Too Large";
  413                 case SC_RANGE_NOT_SATISFIABLE:
  414                         return "Requested Range Not Satisfiable";
  415                 case SC_RESET_CONTENT:
  416                         return "Reset Content";
  417                 case SC_SEE_OTHER:
  418                         return "See Other";
  419                 case SC_SERVICE_UNAVAILABLE:
  420                         return "Service Unavailable";
  421                 case SC_SWITCHING_PROTOCOLS:
  422                         return "Switching Protocols";
  423                 case SC_UNAUTHORIZED:
  424                         return "Unauthorized";
  425                 case SC_UNSUPPORTED_MEDIA_TYPE:
  426                         return "Unsupported Media Type";
  427                 case SC_USE_PROXY:
  428                         return "Use Proxy";
  429                 default: {
  430                         return "Unknown HTTP status";
  431                 }
  432         }
  433 }
  434 
  435 
  436 /* ----------------------------------------------------------------- Private */
  437 
  438 
  439 /**
  440  * Receives standard HTTP requests from a client socket and dispatches
  441  * them to the doXXX methods defined in a cervlet module.
  442  */
  443 static void do_service(Socket_T s) {
  444         volatile HttpResponse res = create_HttpResponse(s);
  445         volatile HttpRequest req = create_HttpRequest(s);
  446         if (res && req) {
  447                 if (Run.httpd.socket.net.ssl.flags & SSL_Enabled)
  448                         set_header(res, "Strict-Transport-Security", "max-age=63072000; includeSubdomains; preload");
  449                 if (is_authenticated(req, res)) {
  450                         set_header(res, "Set-Cookie", "securitytoken=%s; Max-Age=600; HttpOnly; SameSite=strict%s", res->token, (Run.httpd.socket.net.ssl.flags & SSL_Enabled) ? "; Secure" : "");
  451                         if (IS(req->method, METHOD_GET))
  452                                 Impl.doGet(req, res);
  453                         else if (IS(req->method, METHOD_POST))
  454                                 Impl.doPost(req, res);
  455                         else
  456                                 send_error(req, res, SC_NOT_IMPLEMENTED, "Method not implemented");
  457                 }
  458                 send_response(req, res);
  459         }
  460         done(req, res);
  461 }
  462 
  463 
  464 /**
  465  * Return a (RFC1123) Date string
  466  */
  467 static char *get_date(char *result, int size) {
  468         time_t now;
  469         struct tm converted;
  470         time(&now);
  471         if (strftime(result, size, DATEFMT, gmtime_r(&now, &converted)) <= 0)
  472                 *result = 0;
  473         return result;
  474 }
  475 
  476 
  477 /**
  478  * Return this server name + version
  479  */
  480 static char *get_server(char *result, int size) {
  481         snprintf(result, size, "%s %s", SERVER_NAME, Run.httpd.flags & Httpd_Signature ? SERVER_VERSION : "");
  482         return result;
  483 }
  484 
  485 
  486 /**
  487  * Send the response to the client. If the response has already been
  488  * committed, this function does nothing.
  489  */
  490 static void send_response(HttpRequest req, HttpResponse res) {
  491         Socket_T S = res->S;
  492 
  493         if (! res->is_committed) {
  494                 char date[STRLEN];
  495                 char server[STRLEN];
  496 #ifdef HAVE_LIBZ
  497                 const char *acceptEncoding = get_header(req, "Accept-Encoding");
  498                 bool canCompress = acceptEncoding && Str_sub(acceptEncoding, "gzip");
  499 #else
  500                 bool canCompress = false;
  501 #endif
  502                 const void *body = NULL;
  503                 size_t bodyLength = 0;
  504                 if (canCompress && StringBuffer_length(res->outputbuffer) > 0) {
  505                         body = StringBuffer_toCompressed(res->outputbuffer, 6, &bodyLength);
  506                         set_header(res, "Content-Encoding", "gzip");
  507                 } else {
  508                         body = StringBuffer_toString(res->outputbuffer);
  509                         bodyLength = StringBuffer_length(res->outputbuffer);
  510                 }
  511                 char *headers = get_headers(res);
  512                 res->is_committed = true;
  513                 get_date(date, STRLEN);
  514                 get_server(server, STRLEN);
  515                 Socket_print(S, "%s %d %s\r\n", res->protocol, res->status, res->status_msg);
  516                 Socket_print(S, "Date: %s\r\n", date);
  517                 Socket_print(S, "Server: %s\r\n", server);
  518                 Socket_print(S, "Content-Length: %zu\r\n", bodyLength);
  519                 Socket_print(S, "Connection: close\r\n");
  520                 if (headers)
  521                         Socket_print(S, "%s", headers);
  522                 Socket_print(S, "\r\n");
  523                 if (bodyLength)
  524                         if(Socket_write(S, (unsigned char *)body, bodyLength) < 0)
  525                                 Log_error("Http: Cannot send the response -- %s\n", STRERROR);
  526                 FREE(headers);
  527         }
  528 }
  529 
  530 
  531 /* --------------------------------------------------------------- Factories */
  532 
  533 
  534 /**
  535  * Returns a new HttpRequest object wrapping the client request
  536  */
  537 static HttpRequest create_HttpRequest(Socket_T S) {
  538         char line[REQ_STRLEN];
  539         if (Socket_readLine(S, line, sizeof(line)) == NULL) {
  540                 internal_error(S, SC_BAD_REQUEST, "No request found");
  541                 return NULL;
  542         }
  543         Str_chomp(line);
  544         char method[STRLEN];
  545         char url[REQ_STRLEN];
  546         char protocol[STRLEN];
  547         if (sscanf(line, "%255s %1023s HTTP/%3[1.0]", method, url, protocol) != 3) {
  548                 internal_error(S, SC_BAD_REQUEST, "Cannot parse request");
  549                 return NULL;
  550         }
  551         if (strlen(url) >= MAX_URL_LENGTH) {
  552                 internal_error(S, SC_BAD_REQUEST, "[error] URL too long");
  553                 return NULL;
  554         }
  555         HttpRequest req = NULL;
  556         NEW(req);
  557         req->S = S;
  558         Util_urlDecode(url);
  559         req->url = Str_dup(url);
  560         req->method = Str_dup(method);
  561         req->protocol = Str_dup(protocol);
  562         create_headers(req);
  563         if (! create_parameters(req)) {
  564                 destroy_HttpRequest(req);
  565                 internal_error(S, SC_BAD_REQUEST, "Cannot parse Request parameters");
  566                 return NULL;
  567         }
  568         return req;
  569 }
  570 
  571 
  572 /**
  573  * Returns a new HttpResponse object wrapping a default response. Use
  574  * the set_XXX methods to change the object.
  575  */
  576 static HttpResponse create_HttpResponse(Socket_T S) {
  577         HttpResponse res = NULL;
  578         NEW(res);
  579         res->S = S;
  580         res->status = SC_OK;
  581         res->outputbuffer = StringBuffer_create(256);
  582         res->is_committed = false;
  583         res->protocol = SERVER_PROTOCOL;
  584         res->status_msg = get_status_string(SC_OK);
  585         Util_getToken(res->token);
  586         return res;
  587 }
  588 
  589 
  590 /**
  591  * Create HTTP headers for the given request
  592  */
  593 static void create_headers(HttpRequest req) {
  594         char line[REQ_STRLEN] = {0};
  595         while (Socket_readLine(req->S, line, sizeof(line)) && ! (Str_isEqual(line, "\r\n") || Str_isEqual(line, "\n"))) {
  596                 char *value = strchr(line, ':');
  597                 if (value) {
  598                         HttpHeader header = NULL;
  599                         NEW(header);
  600                         *value++ = 0;
  601                         Str_trim(line);
  602                         Str_trim(value);
  603                         Str_chomp(value);
  604                         header->name = Str_dup(line);
  605                         header->value = Str_dup(value);
  606                         header->next = req->headers;
  607                         req->headers = header;
  608                 }
  609         }
  610 }
  611 
  612 
  613 /**
  614  * Create parameters for the given request. Returns false if an error
  615  * occurs.
  616  */
  617 static bool create_parameters(HttpRequest req) {
  618         char *query_string = NULL;
  619         if (IS(req->method, METHOD_POST)) {
  620                 int len;
  621                 const char *content_length = get_header(req, "Content-Length");
  622                 if (! content_length || sscanf(content_length, "%d", &len) != 1 || len < 0 || len > _httpPostLimit)
  623                         return false;
  624                 if (len != 0) {
  625                         query_string = CALLOC(1, _httpPostLimit + 1);
  626                         int n = Socket_read(req->S, query_string, len);
  627                         if (n != len) {
  628                                 FREE(query_string);
  629                                 return false;
  630                         }
  631                 }
  632         } else if (IS(req->method, METHOD_GET)) {
  633                 char *p = strchr(req->url, '?');
  634                 if (p) {
  635                         *p++ = 0;
  636                         query_string = Str_dup(p);
  637                 }
  638         }
  639         if (query_string) {
  640                 if (*query_string) {
  641                         char *p = strchr(query_string, '/');
  642                         if (p) {
  643                                 *p++ = 0;
  644                                 req->pathinfo = Str_dup(p);
  645                         }
  646                         req->params = parse_parameters(query_string);
  647                 }
  648                 FREE(query_string);
  649         }
  650         return true;
  651 }
  652 
  653 
  654 /* ----------------------------------------------------------------- Cleanup */
  655 
  656 
  657 /**
  658  * Clear the response output buffer and headers
  659  */
  660 static void reset_response(HttpResponse res) {
  661         if (res->headers) {
  662                 destroy_entry(res->headers);
  663                 res->headers = NULL; /* Release Pragma */
  664         }
  665         StringBuffer_clear(res->outputbuffer);
  666 }
  667 
  668 
  669 /**
  670  * Finalize the request and response object.
  671  */
  672 static void done(HttpRequest req, HttpResponse res) {
  673         destroy_HttpRequest(req);
  674         destroy_HttpResponse(res);
  675 }
  676 
  677 
  678 /**
  679  * Free a HttpRequest object
  680  */
  681 static void destroy_HttpRequest(HttpRequest req) {
  682         if (req) {
  683                 FREE(req->method);
  684                 FREE(req->url);
  685                 FREE(req->pathinfo);
  686                 FREE(req->protocol);
  687                 FREE(req->remote_user);
  688                 if (req->headers)
  689                         destroy_entry(req->headers);
  690                 if (req->params)
  691                         destroy_entry(req->params);
  692                 FREE(req);
  693         }
  694 }
  695 
  696 
  697 /**
  698  * Free a HttpResponse object
  699  */
  700 static void destroy_HttpResponse(HttpResponse res) {
  701         if (res) {
  702                 StringBuffer_free(&(res->outputbuffer));
  703                 if (res->headers)
  704                         destroy_entry(res->headers);
  705                 FREE(res);
  706         }
  707 }
  708 
  709 
  710 /**
  711  * Free a (linked list of) http entry object(s). Both HttpHeader and
  712  * HttpParameter are of this type.
  713  */
  714 static void destroy_entry(void *p) {
  715         struct entry *h = p;
  716         if (h->next)
  717                 destroy_entry(h->next);
  718         FREE(h->name);
  719         FREE(h->value);
  720         FREE(h);
  721 }
  722 
  723 
  724 /* ----------------------------------------------------- Checkers/Validators */
  725 
  726 
  727 static bool _isCookieSeparator(int c) {
  728         return (c == ' ' || c == '\n' || c == ';' || c == ',');
  729 }
  730 
  731 
  732 static bool is_authenticated(HttpRequest req, HttpResponse res) {
  733         if (Run.httpd.credentials) {
  734                 if (! basic_authenticate(req)) {
  735                         // Send just generic error message to the client to not disclose e.g. username existence in case of credentials harvesting attack
  736                         send_error(req, res, SC_UNAUTHORIZED, "You are not authorized to access monit. Either you supplied the wrong credentials (e.g. bad password), or your browser doesn't understand how to supply the credentials required");
  737                         set_header(res, "WWW-Authenticate", "Basic realm=\"monit\"");
  738                         return false;
  739                 }
  740         }
  741         if (IS(req->method, METHOD_POST)) {
  742                 // Check CSRF double-submit cookie (https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Double_Submit_Cookie)
  743                 const char *token = get_parameter(req, "securitytoken");
  744                 if (! token) {
  745                         Log_error("HttpRequest: access denied -- client [%s]: missing CSRF token in HTTP parameter\n", NVLSTR(Socket_getRemoteHost(req->S)));
  746                         send_error(req, res, SC_FORBIDDEN, "Invalid CSRF Token");
  747                         return false;
  748                 }
  749                 const char *cookie = get_header(req, "Cookie");
  750                 if (! cookie) {
  751                         Log_error("HttpRequest: access denied -- client [%s]: missing CSRF token cookie\n", NVLSTR(Socket_getRemoteHost(req->S)));
  752                         send_error(req, res, SC_FORBIDDEN, "Invalid CSRF Token");
  753                         return false;
  754                 }
  755                 const char *cookieName = "securitytoken=";
  756                 for (int i = 0, j = 0; cookie[i]; i++) {
  757                         if (_isCookieSeparator(cookie[i])) {
  758                                 // Cookie separator
  759                                 j = 0;
  760                                 continue;
  761                         }
  762                         if (j < 14) {
  763                                 // Cookie name
  764                                 if (cookie[i] == cookieName[j]) {
  765                                         j++;
  766                                         continue;
  767                                 } else {
  768                                         j = 0;
  769                                 }
  770                         } else if (j == 14) {
  771                                 // Cookie value
  772                                 char cookieValue[STRLEN] = {};
  773                                 strncpy(cookieValue, cookie + i, sizeof(cookieValue) - 1);
  774                                 for (int k = 0; cookieValue[k]; k++) {
  775                                         if (_isCookieSeparator(cookieValue[k])) {
  776                                                 cookieValue[k] = 0;
  777                                                 break;
  778                                         }
  779                                 }
  780                                 if (Str_compareConstantTime(cookieValue, token)) {
  781                                         Log_error("HttpRequest: access denied -- client [%s]: CSRF token mismatch\n", NVLSTR(Socket_getRemoteHost(req->S)));
  782                                         send_error(req, res, SC_FORBIDDEN, "Invalid CSRF Token");
  783                                         return false;
  784                                 }
  785                                 return true;
  786                         }
  787                 }
  788                 Log_error("HttpRequest: access denied -- client [%s]: no CSRF token in cookie\n", NVLSTR(Socket_getRemoteHost(req->S)));
  789                 send_error(req, res, SC_FORBIDDEN, "Invalid CSRF Token");
  790                 return false;
  791         }
  792         return true;
  793 }
  794 
  795 
  796 /**
  797  * Authenticate the basic-credentials (uname/password) submitted by
  798  * the user.
  799  */
  800 static bool basic_authenticate(HttpRequest req) {
  801         const char *credentials = get_header(req, "Authorization");
  802         if (! (credentials && Str_startsWith(credentials, "Basic "))) {
  803                 Log_debug("HttpRequest: access denied -- client [%s]: missing or invalid Authorization header\n", NVLSTR(Socket_getRemoteHost(req->S)));
  804                 return false;
  805         }
  806         char buf[STRLEN] = {0};
  807         strncpy(buf, &credentials[6], sizeof(buf) - 1);
  808         char uname[STRLEN] = {0};
  809         if (decode_base64((unsigned char *)uname, buf) <= 0) {
  810                 Log_debug("HttpRequest: access denied -- client [%s]: invalid Authorization header\n", NVLSTR(Socket_getRemoteHost(req->S)));
  811                 return false;
  812         }
  813         if (STR_UNDEF(uname)) {
  814                 Log_debug("HttpRequest: access denied -- client [%s]: empty username\n", NVLSTR(Socket_getRemoteHost(req->S)));
  815                 return false;
  816         }
  817         char *password = strchr(uname, ':');
  818         if (STR_UNDEF(password)) {
  819                 Log_debug("HttpRequest: access denied -- client [%s]: empty password\n", NVLSTR(Socket_getRemoteHost(req->S)));
  820                 return false;
  821         }
  822         *password++ = 0;
  823         /* Check if user exist */
  824         if (! Util_getUserCredentials(uname)) {
  825                 Log_error("HttpRequest: access denied -- client [%s]: unknown user '%s'\n", NVLSTR(Socket_getRemoteHost(req->S)), uname);
  826                 return false;
  827         }
  828         /* Check if user has supplied the right password */
  829         if (! Util_checkCredentials(uname,  password)) {
  830                 Log_error("HttpRequest: access denied -- client [%s]: wrong password for user '%s'\n", NVLSTR(Socket_getRemoteHost(req->S)), uname);
  831                 return false;
  832         }
  833         req->remote_user = Str_dup(uname);
  834         return true;
  835 }
  836 
  837 
  838 /* --------------------------------------------------------------- Utilities */
  839 
  840 
  841 /**
  842  * Send an error message to the client. This is a helper function,
  843  * used internal if the service function fails to setup the framework
  844  * properly; i.e. with a valid HttpRequest and a valid HttpResponse.
  845  */
  846 static void internal_error(Socket_T S, int status, const char *msg) {
  847         char date[STRLEN];
  848         char server[STRLEN];
  849         const char *status_msg = get_status_string(status);
  850 
  851         get_date(date, STRLEN);
  852         get_server(server, STRLEN);
  853         Socket_print(S,
  854                      "%s %d %s\r\n"
  855                      "Date: %s\r\n"
  856                      "Server: %s\r\n"
  857                      "Content-Type: text/html\r\n"
  858                      "Connection: close\r\n"
  859                      "\r\n"
  860                      "<html><head><title>%s</title></head>"
  861                      "<body bgcolor=#FFFFFF><h2>%s</h2>%s<p>"
  862                      "<hr><a href='%s'><font size=-1>%s</font></a>"
  863                      "</body></html>\r\n",
  864                      SERVER_PROTOCOL, status, status_msg, date, server,
  865                      status_msg, status_msg, msg, SERVER_URL, server);
  866         DEBUG("HttpRequest: error -- client [%s]: %s %d %s\n", NVLSTR(Socket_getRemoteHost(S)), SERVER_PROTOCOL, status, msg ? msg : status_msg);
  867 }
  868 
  869 
  870 /**
  871  * Parse request parameters from the given query string and return a
  872  * linked list of HttpParameters
  873  */
  874 static HttpParameter parse_parameters(char *query_string) {
  875 #define KEY 1
  876 #define VALUE 2
  877         int token;
  878         int cursor = 0;
  879         char *key = NULL;
  880         char *value = NULL;
  881         HttpParameter head = NULL;
  882 
  883         while ((token = get_next_token(query_string, &cursor, &value))) {
  884                 if (token == KEY)
  885                         key = value;
  886                 else if (token == VALUE) {
  887                         HttpParameter p = NULL;
  888                         if (! key)
  889                                 goto error;
  890                         NEW(p);
  891                         p->name = key;
  892                         p->value = Util_urlDecode(value);
  893                         p->next = head;
  894                         head = p;
  895                         key = NULL;
  896                 }
  897         }
  898         if (key)
  899                 FREE(key);
  900         return head;
  901 error:
  902         FREE(key);
  903         FREE(value);
  904         if (head != NULL)
  905                 destroy_entry(head);
  906         return NULL;
  907 }
  908 
  909 
  910 /**
  911  * A mini-scanner for tokenizing a query string
  912  */
  913 static int get_next_token(char *s, int *cursor, char **r) {
  914         int i = *cursor;
  915 
  916         while (s[*cursor]) {
  917                 if (s[*cursor+1] == '=') {
  918                         *cursor += 1;
  919                         *r = Str_ndup(&s[i], (*cursor-i));
  920                         return KEY;
  921                 }
  922                 if (s[*cursor] == '=') {
  923                         while (s[*cursor] && s[*cursor] != '&') *cursor += 1;
  924                         if (s[*cursor] == '&') {
  925                                 *r = Str_ndup(&s[i+1], (*cursor-i)-1);
  926                                 *cursor += 1;
  927                         }  else {
  928                                 *r = Str_ndup(&s[i+1], (*cursor-i));
  929                         }
  930                         return VALUE;
  931                 }
  932                 *cursor += 1;
  933         }
  934         return 0;
  935 }
  936