Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
[privoxy.git] / cgi.c
diff --git a/src/cgi.c b/cgi.c
similarity index 87%
rename from src/cgi.c
rename to cgi.c
index 034f872..1db93f8 100644 (file)
--- a/src/cgi.c
+++ b/cgi.c
@@ -1,7 +1,7 @@
-const char cgi_rcs[] = "$Id: cgi.c,v 2.3 2002/11/12 16:19:18 oes Exp $";
+const char cgi_rcs[] = "$Id: cgi.c,v 1.70.2.13 2004/02/17 13:30:23 oes Exp $";
 /*********************************************************************
  *
- * File        :  $Source: /cvsroot/ijbswa/current/src/cgi.c,v $
+ * File        :  $Source: /cvsroot/ijbswa/current/Attic/cgi.c,v $
  *
  * Purpose     :  Declares functions to intercept request, generate
  *                html or gif answers, and to compose HTTP resonses.
@@ -38,19 +38,71 @@ const char cgi_rcs[] = "$Id: cgi.c,v 2.3 2002/11/12 16:19:18 oes Exp $";
  *
  * Revisions   :
  *    $Log: cgi.c,v $
- *    Revision 2.3  2002/11/12 16:19:18  oes
- *    Fix: g_bToggleIJB was used outside #ifdef FEATURE_TOGGLE
- *
- *    Revision 2.2  2002/09/04 15:17:28  oes
- *    Synced with the stable branch:
- *        Revision 1.70.2.1  2002/08/05 11:17:46  oes
- *        Fixed Bug #587820, i.e. added workaround for IE bug that includes fragment identifier in (cgi) query
- *
- *    Revision 2.1  2002/06/04 17:55:24  jongfoster
- *    Adding comments
- *
- *    Revision 2.0  2002/06/04 14:34:21  jongfoster
- *    Moving source files to src/
+ *    Revision 1.70.2.13  2004/02/17 13:30:23  oes
+ *    Moved cgi_error_disabled() from cgiedit.c to
+ *    cgi.c to re-enable build with --disable-editor.
+ *    Fixes Bug #892744. Thanks to Matthew Fischer
+ *    for spotting.
+ *
+ *    Revision 1.70.2.12  2003/12/17 16:33:16  oes
+ *     - Added new function cgi_redirect to handle creation of
+ *       HTTP redirect messages formerly repeated in the code.
+ *     - Send cgi_error_disabled instead of cgi_error_404 when
+ *       referrer check fails
+ *     - Dynamic content now gets Expires header field with date
+ *       in the past
+ *
+ *    Revision 1.70.2.11  2003/10/23 12:29:26  oes
+ *    Bugfix: Transparent PNG was not transparent. Thanks to
+ *    Dan Razzell of Starfish Systems for notice and new PNG.
+ *
+ *    Revision 1.70.2.10  2003/06/06 07:54:25  oes
+ *    Security fix: dspatch_known_cgi no longer considers an empty
+ *    referrer safe for critical CGIs, since malicious links could
+ *    reside on https:// locations which browsers don't advertize as
+ *    referrers. Closes bug #749916, thanks to Jeff Epler for the
+ *    hint. Goodbye One-Click[tm] toggling :-(
+ *
+ *    Revision 1.70.2.9  2003/05/08 15:11:31  oes
+ *    Nit
+ *
+ *    Revision 1.70.2.8  2003/04/29 13:33:51  oes
+ *    Killed a compiler warning on OSX
+ *
+ *    Revision 1.70.2.7  2003/04/03 13:50:58  oes
+ *    - Don't call cgi_error_disabled ifndef FEATURE_CGI_EDIT_ACTIONS
+ *      (fixes bug #710056)
+ *    - Show toggle info only if we have it
+ *
+ *    Revision 1.70.2.6  2003/03/12 01:26:25  david__schmidt
+ *    Move declaration of struct tm dummy outside of a control block so it is
+ *    accessible later on during snprintf in get_http_time.
+ *
+ *    Revision 1.70.2.5  2003/03/11 11:53:58  oes
+ *    Cosmetic: Renamed cryptic variable
+ *
+ *    Revision 1.70.2.4  2003/03/07 03:41:03  david__schmidt
+ *    Wrapping all *_r functions (the non-_r versions of them) with mutex semaphores for OSX.  Hopefully this will take care of all of those pesky crash reports.
+ *
+ *    Revision 1.70.2.3  2002/11/28 18:14:32  oes
+ *    Disable access to critical CGIs via untrusted referrers.
+ *    This prevents users from being tricked by malicious websites
+ *    into making unintentional configuration changes:
+ *
+ *     - Added flag to each cgi_dispatcher that allows or denies
+ *       external linking
+ *     - Introduced proviorical function that greps for the
+ *       referrer header before regular header parsing happens
+ *     - Added safety check to dispatch_known_cgi. CGI is called
+ *       if (cgi harmless || no referrer || we are referrer).
+ *       Else a) toggle calls are modified not to change status and
+ *       b) all other calls are denied.
+ *
+ *    Revision 1.70.2.2  2002/11/12 16:20:37  oes
+ *    Added missing #ifdef FEATURE_TOGGLE around g_bToggleIJB; fixes bug #636651
+ *
+ *    Revision 1.70.2.1  2002/08/05 11:17:46  oes
+ *    Fixed Bug #587820, i.e. added workaround for IE bug that includes fragment identifier in (cgi) query
  *
  *    Revision 1.70  2002/05/19 11:33:20  jongfoster
  *    If a CGI error was not handled, and propogated back to
@@ -428,126 +480,138 @@ const char cgi_rcs[] = "$Id: cgi.c,v 2.3 2002/11/12 16:19:18 oes Exp $";
 #include "cgiedit.h"
 #endif /* def FEATURE_CGI_EDIT_ACTIONS */
 #include "loadcfg.h"
-/* loadcfg.h is for g_bToggleIJB only */
+/* loadcfg.h is for global_toggle_state only */
 #ifdef FEATURE_PTHREAD
 #include <pthread.h>
 #include "jcc.h"
 /* jcc.h is for mutex semaphore globals only */
 #endif /* def FEATURE_PTHREAD */
-
 const char cgi_h_rcs[] = CGI_H_VERSION;
 
-/**
- * List of CGI functions: name, handler, description.
+/*
+ * List of CGI functions: name, handler, description
  * Note: Do NOT use single quotes in the description;
  *       this will break the dynamic "blocked" template!
  */
 static const struct cgi_dispatcher cgi_dispatchers[] = {
    { "",
          cgi_default,
-         "Privoxy main page" },
+         "Privoxy main page",
+         TRUE },
 #ifdef FEATURE_GRACEFUL_TERMINATION
    { "die", 
          cgi_die,  
          "<b>Shut down</b> - <em class=\"warning\">Do not deploy this build in a production environment, "
-         "this is a one click Denial Of Service attack!!!</em>" }, 
+        "this is a one click Denial Of Service attack!!!</em>",
+         FALSE }, 
 #endif
    { "show-status", 
          cgi_show_status,  
-         "View & change the current configuration" }, 
+#ifdef FEATURE_CGI_EDIT_ACTIONS
+        "View & change the current configuration",
+#else
+        "View the current configuration",
+#endif
+         TRUE }, 
    { "show-version", 
          cgi_show_version,  
-         "View the source code version numbers" }, 
+         "View the source code version numbers",
+          TRUE }, 
    { "show-request", 
          cgi_show_request,  
-         "View the request headers." }, 
+         "View the request headers.",
+         TRUE }, 
    { "show-url-info",
          cgi_show_url_info, 
-         "Look up which actions apply to a URL and why"  },
+         "Look up which actions apply to a URL and why",
+         TRUE },
 #ifdef FEATURE_CGI_EDIT_ACTIONS
    { "toggle",
          cgi_toggle, 
-         "Toggle Privoxy on or off" },
-
+         "Toggle Privoxy on or off",
+         FALSE },
    { "edit-actions", /* Edit the actions list */
          cgi_edit_actions, 
-         NULL },
+         NULL, FALSE },
    { "eaa", /* Shortcut for edit-actions-add-url-form */
          cgi_edit_actions_add_url_form, 
-         NULL },
+         NULL, FALSE },
    { "eau", /* Shortcut for edit-actions-url-form */
          cgi_edit_actions_url_form, 
-         NULL },
+         NULL, FALSE },
    { "ear", /* Shortcut for edit-actions-remove-url-form */
          cgi_edit_actions_remove_url_form, 
-         NULL },
+         NULL, FALSE },
+   { "eal", /* Shortcut for edit-actions-list */
+         cgi_edit_actions_list, 
+         NULL, FALSE },
    { "eafu", /* Shortcut for edit-actions-for-url */
          cgi_edit_actions_for_url, 
-         NULL },
+         NULL, FALSE },
    { "eas", /* Shortcut for edit-actions-submit */
          cgi_edit_actions_submit, 
-         NULL },
+         NULL, FALSE },
    { "easa", /* Shortcut for edit-actions-section-add */
          cgi_edit_actions_section_add, 
-         NULL },
+         NULL, FALSE  },
    { "easr", /* Shortcut for edit-actions-section-remove */
          cgi_edit_actions_section_remove, 
-         NULL },
+         NULL, FALSE  },
    { "eass", /* Shortcut for edit-actions-section-swap */
          cgi_edit_actions_section_swap, 
-         NULL },
+         NULL, FALSE  },
    { "edit-actions-for-url",
          cgi_edit_actions_for_url, 
-         NULL /* Edit the actions for (a) specified URL(s) */ },
+         NULL, FALSE  /* Edit the actions for (a) specified URL(s) */ },
    { "edit-actions-list",
          cgi_edit_actions_list, 
-         NULL /* Edit the actions list */ },
+         NULL, TRUE /* Edit the actions list */ },
    { "edit-actions-submit",
          cgi_edit_actions_submit, 
-         NULL /* Change the actions for (a) specified URL(s) */ },
+         NULL, FALSE /* Change the actions for (a) specified URL(s) */ },
    { "edit-actions-url",
          cgi_edit_actions_url, 
-         NULL /* Change a URL pattern in the actionsfile */ },
+         NULL, FALSE /* Change a URL pattern in the actionsfile */ },
    { "edit-actions-url-form",
          cgi_edit_actions_url_form, 
-         NULL /* Form to change a URL pattern in the actionsfile */ },
+         NULL, FALSE /* Form to change a URL pattern in the actionsfile */ },
    { "edit-actions-add-url",
          cgi_edit_actions_add_url, 
-         NULL /* Add a URL pattern to the actionsfile */ },
+         NULL, FALSE /* Add a URL pattern to the actionsfile */ },
    { "edit-actions-add-url-form",
          cgi_edit_actions_add_url_form, 
-         NULL /* Form to add a URL pattern to the actionsfile */ },
+         NULL, FALSE /* Form to add a URL pattern to the actionsfile */ },
    { "edit-actions-remove-url",
          cgi_edit_actions_remove_url, 
-         NULL /* Remove a URL pattern from the actionsfile */ },
+         NULL, FALSE /* Remove a URL pattern from the actionsfile */ },
    { "edit-actions-remove-url-form",
          cgi_edit_actions_remove_url_form, 
-         NULL /* Form to remove a URL pattern from the actionsfile */ },
+         NULL, FALSE /* Form to remove a URL pattern from the actionsfile */ },
    { "edit-actions-section-add",
          cgi_edit_actions_section_add, 
-         NULL /* Remove a section from the actionsfile */ },
+         NULL, FALSE /* Remove a section from the actionsfile */ },
    { "edit-actions-section-remove",
          cgi_edit_actions_section_remove, 
-         NULL /* Remove a section from the actionsfile */ },
+         NULL, FALSE /* Remove a section from the actionsfile */ },
    { "edit-actions-section-swap",
          cgi_edit_actions_section_swap, 
-         NULL /* Swap two sections in the actionsfile */ },
+         NULL, FALSE /* Swap two sections in the actionsfile */ },
 #endif /* def FEATURE_CGI_EDIT_ACTIONS */
    { "robots.txt", 
          cgi_robots_txt,  
-         NULL /* Sends a robots.txt file to tell robots to go away. */ }, 
+         NULL, TRUE /* Sends a robots.txt file to tell robots to go away. */ }, 
    { "send-banner",
          cgi_send_banner, 
-         NULL /* Send a built-in image */ },
+         NULL, TRUE /* Send a built-in image */ },
    { "send-stylesheet",
          cgi_send_stylesheet, 
-         NULL /* Send templates/cgi-style.css */ },
+         NULL, TRUE /* Send templates/cgi-style.css */ },
    { "t",
          cgi_transparent_image, 
-         NULL /* Send a transparent image (short name) */ },
+         NULL, TRUE /* Send a transparent image (short name) */ },
    { NULL, /* NULL Indicates end of list and default page */
          cgi_error_404,
-         NULL /* Unknown CGI page */ }
+         NULL, TRUE /* Unknown CGI page */ }
 };
 
 
@@ -577,17 +641,15 @@ const char image_pattern_data[] =
  */
 const char image_blank_data[] =
  "\211\120\116\107\015\012\032\012\000\000\000\015\111\110\104\122"
- "\000\000\000\004\000\000\000\004\010\006\000\000\000\251\361\236"
- "\176\000\000\000\007\164\111\115\105\007\322\003\013\020\073\070"
- "\013\025\036\203\000\000\000\011\160\110\131\163\000\000\013\022"
- "\000\000\013\022\001\322\335\176\374\000\000\000\004\147\101\115"
- "\101\000\000\261\217\013\374\141\005\000\000\000\033\111\104\101"
- "\124\170\332\143\070\161\342\304\207\377\377\377\347\302\150\006"
- "\144\016\210\146\040\250\002\000\042\305\065\221\270\027\131\110"
+ "\000\000\000\001\000\000\000\001\001\003\000\000\000\045\333\126"
+ "\312\000\000\000\003\120\114\124\105\377\377\377\247\304\033\310"
+ "\000\000\000\001\164\122\116\123\000\100\346\330\146\000\000\000"
+ "\001\142\113\107\104\000\210\005\035\110\000\000\000\012\111\104"
+ "\101\124\170\001\143\140\000\000\000\002\000\001\163\165\001\030"
  "\000\000\000\000\111\105\116\104\256\102\140\202";
 #else
 
-/**
+/*
  * Checkerboard pattern, as a GIF.
  */
 const char image_pattern_data[] =
@@ -597,7 +659,7 @@ const char image_pattern_data[] =
    "\054\000\000\000\000\004\000\004\000\000\002\005\104\174\147"
    "\270\005\000\073";
 
-/**
+/*
  * 1x1 transparant GIF.
  */
 const char image_blank_data[] =
@@ -606,27 +668,12 @@ const char image_blank_data[] =
    "\000\001\000\000\002\002D\001\000;";
 #endif
 
-/**
- * The size of the image_pattern, in bytes.
- */
 const size_t image_pattern_length = sizeof(image_pattern_data) - 1;
-
-/**
- * The size of the image_blank, in bytes.
- */
 const size_t image_blank_length   = sizeof(image_blank_data) - 1;
 
 
-/**
- * The "Out of memory" CGI response.  This is statically allocated
- * and is initialized at startup, for obvious reasons.  It's
- * read-only.  finish_http_response() and free_http_response()
- * have been special-cased to do nothing if they are passed
- * this structure.
- */
 static struct http_response cgi_error_memory_response[1];
 
-
 static struct http_response *dispatch_known_cgi(struct client_state * csp,
                                                 const char * path);
 static struct map *parse_cgi_parameters(char *argstring);
@@ -683,7 +730,7 @@ struct http_response *dispatch_cgi(struct client_state *csp)
       else if (*path != '\0')
       {
          /*
-          * wierdness: URL is /configXXX, where XXX is some string
+          * weirdness: URL is /configXXX, where XXX is some string
           * Do *NOT* intercept.
           */
          return NULL;
@@ -703,6 +750,42 @@ struct http_response *dispatch_cgi(struct client_state *csp)
 }
 
 
+/*********************************************************************
+ *
+ * Function    :  grep_cgi_referrer
+ *
+ * Description :  Ugly provisorical fix that greps the value of the
+ *                referer HTTP header field out of a linked list of
+ *                strings like found at csp->headers. Will disappear
+ *                in Privoxy 3.1.
+ *
+ *                FIXME: csp->headers ought to be csp->http->headers
+ *                FIXME: Parsing all client header lines should
+ *                       happen right after the request is received!
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  pointer to value (no copy!), or NULL if none found.
+ *
+ *********************************************************************/
+char *grep_cgi_referrer(struct client_state *csp)
+{
+   struct list_entry *p;
+
+   for (p = csp->headers->first; p != NULL; p = p->next)
+   {
+      if (p->str == NULL) continue;
+      if (strncmpic(p->str, "Referer: ", 9) == 0)
+      {
+         return ((p->str) + 9);
+      }
+   }
+   return NULL;
+
+}
+
+
 /*********************************************************************
  * 
  * Function    :  dispatch_known_cgi
@@ -731,6 +814,7 @@ static struct http_response *dispatch_known_cgi(struct client_state * csp,
    struct http_response *rsp;
    char *query_args_start;
    char *path_copy;
+   char *referrer;
    jb_err err;
 
    if (NULL == (path_copy = strdup(path)))
@@ -754,7 +838,6 @@ static struct http_response *dispatch_known_cgi(struct client_state * csp,
       return cgi_error_memory();
    }
 
-
    /*
     * At this point:
     * path_copy        = CGI call name
@@ -773,15 +856,45 @@ static struct http_response *dispatch_known_cgi(struct client_state * csp,
    log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 3", 
                             csp->ip_addr_str, csp->http->cmd); 
 
-   /* Find and start the right CGI function*/
+   /* 
+    * Find and start the right CGI function
+    */
    d = cgi_dispatchers;
    for (;;)
    {
       if ((d->name == NULL) || (strcmp(path_copy, d->name) == 0))
       {
-         err = (d->handler)(csp, rsp, param_list);
+         /*
+          * If the called CGI is either harmless, or referred
+          * from a trusted source, start it.
+          */
+         if (d->harmless
+             || ((NULL != (referrer = grep_cgi_referrer(csp)))
+                 && (0 == strncmp(referrer, "http://config.privoxy.org/", 26)))
+             )
+         {
+            err = (d->handler)(csp, rsp, param_list);
+         }
+         else
+         {
+            /*
+             * Else, modify toggle calls so that they only display
+             * the status, and deny all other calls.
+             */
+            if (0 == strcmp(path_copy, "toggle"))
+            {
+               unmap(param_list, "set");
+               err = (d->handler)(csp, rsp, param_list);
+            }
+            else
+            {
+               err = cgi_error_disabled(csp, rsp);
+            }
+         }
+
          free(path_copy);
          free_map(param_list);
+
          if (err == JB_ERR_CGI_PARAMS)
          {
             err = cgi_error_bad_param(csp, rsp);
@@ -807,8 +920,8 @@ static struct http_response *dispatch_known_cgi(struct client_state * csp,
       d++;
    }
 }
-
-
+   
+        
 /*********************************************************************
  *
  * Function    :  parse_cgi_parameters
@@ -1135,6 +1248,42 @@ struct http_response *error_response(struct client_state *csp,
 }
 
 
+/*********************************************************************
+ *
+ * Function    :  cgi_error_disabled
+ *
+ * Description :  CGI function that is called to generate an error
+ *                response if the actions editor or toggle CGI are
+ *                accessed despite having being disabled at compile-
+ *                or run-time.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  rsp = http_response data structure for output
+ *
+ * CGI Parameters : none
+ *
+ * Returns     :  JB_ERR_OK on success
+ *                JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err cgi_error_disabled(struct client_state *csp,
+                          struct http_response *rsp)
+{
+   struct map *exports;
+
+   assert(csp);
+   assert(rsp);
+
+   if (NULL == (exports = default_exports(csp, NULL)))
+   {
+      return JB_ERR_MEMORY;
+   }
+
+   return template_fill_for_cgi(csp, "cgi-error-disabled", exports, rsp);
+}
+
+
 /*********************************************************************
  *
  * Function    :  cgi_init_error_messages
@@ -1382,6 +1531,42 @@ jb_err cgi_error_bad_param(struct client_state *csp,
 }
 
 
+/*********************************************************************
+ *
+ * Function    :  cgi_redirect 
+ *
+ * Description :  CGI support function to generate a HTTP redirect
+ *                message
+ *
+ * Parameters  :
+ *          1  :  rsp = http_response data structure for output
+ *          2  :  target = string with the target URL
+ *
+ * CGI Parameters : None
+ *
+ * Returns     :  JB_ERR_OK on success
+ *                JB_ERR_MEMORY on out-of-memory error.  
+ *
+ *********************************************************************/
+jb_err cgi_redirect (struct http_response * rsp, const char *target)
+{
+   jb_err err;
+
+   assert(rsp);
+   assert(target);
+
+   err = enlist_unique_header(rsp->headers, "Location", target);
+
+   rsp->status = strdup("302 Local Redirect from Privoxy");
+   if (rsp->status == NULL)
+   {
+      return JB_ERR_MEMORY;
+   }
+
+   return err;
+}
+
+
 /*********************************************************************
  *
  * Function    :  add_help_link
@@ -1587,7 +1772,7 @@ struct http_response *finish_http_response(struct http_response *rsp)
       get_http_time(0, buf);
       if (!err) err = enlist_unique_header(rsp->headers, "Date", buf);
       if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", buf);
-      if (!err) err = enlist_unique_header(rsp->headers, "Expires", buf);
+      if (!err) err = enlist_unique_header(rsp->headers, "Expires", "Sat, 17 Jun 2000 12:00:00 GMT");
    }
 
 
@@ -1989,7 +2174,9 @@ struct map *default_exports(const struct client_state *csp, const char *caller)
    if (!err) err = map(exports, "user-manual",   1, csp->config->usermanual ,1);
    if (!err) err = map(exports, "actions-help-prefix", 1, ACTIONS_HELP_PREFIX ,1);
 #ifdef FEATURE_TOGGLE
-   if (!err) err = map_conditional(exports, "enabled-display", g_bToggleIJB);
+   if (!err) err = map_conditional(exports, "enabled-display", global_toggle_state);
+#else
+   if (!err) err = map_block_killer(exports, "can-toggle");
 #endif
 
    snprintf(buf, 20, "%d", csp->config->hport);