]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
PR64785: mod_allowmethods: Allow methods to be added/removed with +/- prefix
authorEric Covener <covener@apache.org>
Sun, 8 Nov 2020 15:25:15 +0000 (15:25 +0000)
committerEric Covener <covener@apache.org>
Sun, 8 Nov 2020 15:25:15 +0000 (15:25 +0000)
Committed By: covener
Submitted By: Marcel Montes <spiceman gmail.com>

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1883203 13f79535-47bb-0310-9956-ffa450edef68

changes-entries/pr64785.txt [new file with mode: 0644]
docs/manual/mod/mod_allowmethods.xml
modules/aaa/mod_allowmethods.c

diff --git a/changes-entries/pr64785.txt b/changes-entries/pr64785.txt
new file mode 100644 (file)
index 0000000..4f015c4
--- /dev/null
@@ -0,0 +1,3 @@
+  *) mod_allowmethods: Allow methods to be added/removed with +/- prefix.  PR64785.
+     [Marcel Montes <spiceman gmail.com>]
+
index 42e0aa5f52a10e969ac69e57f74a9f5ede0ca444..33944f46ef5023e404a569d8edf12b33865b199f 100644 (file)
@@ -46,6 +46,10 @@ used on a server. The most common configuration would be:</p>
 &lt;Location "/"&gt;
    AllowMethods GET POST OPTIONS
 &lt;/Location&gt;
+
+&lt;Location "/nopost"&gt;
+   AllowMethods -POST
+&lt;/Location&gt;
 </highlight>
 
 </summary>
@@ -53,11 +57,12 @@ used on a server. The most common configuration would be:</p>
 <directivesynopsis>
 <name>AllowMethods</name>
 <description>Restrict access to the listed HTTP methods</description>
-<syntax>AllowMethods reset|<em>HTTP-method</em>
-[<em>HTTP-method</em>]...</syntax>
+<syntax>AllowMethods reset | [+|-]<var>HTTP-method</var>
+[ [+|-]<var>HTTP-method</var> ] ...</syntax>
 <default>AllowMethods reset</default>
 <contextlist><context>directory</context></contextlist>
 <status>Experimental</status>
+<compatibility>+/- added in 2.5.1</compatibility>
 
 <usage>
 
@@ -77,9 +82,28 @@ turn off <module>mod_allowmethods</module> in a deeper nested context:</p>
   use <directive module="core">TraceEnable</directive> instead.</p>
 </note>
 
+<p>Normally, if multiple <directive>AllowMethods</directive> could
+apply to a directory, then the most specific one is used and
+others are ignored; the methods are not merged. (See <a
+href="../sections.html#merging">how sections are merged</a>.)
+However if <em>all</em> the methods on the
+<directive>AllowMethods</directive> directive are preceded by a
+<code>+</code> or <code>-</code> symbol, the options are
+merged. Any method preceded by a <code>+</code> are added to the
+methods currently in force, and any method preceded by a
+<code>-</code> are removed from the methods currently in
+force. </p>
+
+<note><title>Note</title>
+<p>Mixing <directive>AllowMethods</directive> with a <code>+</code> or
+<code>-</code> with those without is not valid syntax and will be
+rejected during server startup by the syntax check with an abort.</p>
+</note>
+
 <p><module>mod_allowmethods</module> was written to replace the rather
 kludgy implementation of <directive module="core">Limit</directive> and
 <directive module="core">LimitExcept</directive>.</p>
+
 </usage>
 </directivesynopsis>
 
index 3062efd323f7e3e48e949ffef9815b97e89b2354..7e490593c6457923f9c78c3ac4f9a165e062782f 100644 (file)
  */
 
 typedef struct am_conf_t {
-    int allowed_set;
-    ap_method_mask_t allowed;
+    int allowed_set;            /* AllowMethods has been set/changed flag */
+    int enforce_methods;        /* Enforce AllowMethods flag              */
+    ap_method_mask_t add;       /* Methods Added by +METHOD mask          */
+    ap_method_mask_t remove;    /* Methods Removed by -METHOD mask        */
+    ap_method_mask_t allowed;   /* Allowed Methods mask                   */
 } am_conf_t;
 
 module AP_MODULE_DECLARE_DATA allowmethods_module;
@@ -57,7 +60,8 @@ static int am_check_access(request_rec *r)
 
     conf = (am_conf_t *) ap_get_module_config(r->per_dir_config,
                                               &allowmethods_module);
-    if (!conf || conf->allowed == 0) {
+
+    if (!conf || conf->enforce_methods == 0) {
         return DECLINED;
     }
 
@@ -80,8 +84,11 @@ static void *am_create_conf(apr_pool_t *p, char *dummy)
 {
     am_conf_t *conf = apr_pcalloc(p, sizeof(am_conf_t));
 
-    conf->allowed = 0;
-    conf->allowed_set = 0;
+    conf->allowed         = INT_MAX;
+    conf->allowed_set     = 0;
+    conf->add             = 0;
+    conf->remove          = 0;
+    conf->enforce_methods = 0;
     return conf;
 }
 
@@ -92,12 +99,37 @@ static void *am_merge_conf(apr_pool_t *pool, void *a, void *b)
     am_conf_t *conf = apr_palloc(pool, sizeof(am_conf_t));
 
     if (add->allowed_set) {
-        conf->allowed = add->allowed;
-        conf->allowed_set = add->allowed_set;
+        conf->add    = 0;
+        conf->remove = 0;
+
+        /* Add/Remove AllowedMethods set with + or - */
+        if( add->add || add->remove ) {
+            conf->allowed = base->allowed | add->allowed;
+
+            if( add->add ) {
+                conf->allowed |= add->add;
+            }
+            if( add->remove ) {
+                conf->allowed &= ~(add->remove);
+            }
+
+            conf->enforce_methods = 1;
+            conf->allowed_set     = 1;
+        }
+        /* Straightforward AllowMethods settings, just set it */
+        else
+        {
+            conf->allowed          = add->allowed;
+            conf->allowed_set      = add->allowed_set;
+            conf->enforce_methods  = add->enforce_methods;
+        }
     }
     else {
-        conf->allowed = base->allowed;
-        conf->allowed_set = base->allowed_set;
+        conf->allowed         = base->allowed;
+        conf->add             = base->add;
+        conf->remove          = base->remove;
+        conf->allowed_set     = base->allowed_set;
+        conf->enforce_methods = base->enforce_methods;
     }
 
     return conf;
@@ -108,30 +140,75 @@ static const char *am_allowmethods(cmd_parms *cmd, void *d, int argc,
 {
     int i;
     am_conf_t *conf = (am_conf_t *)d;
+    int merge = 0;
+    int first = 1;
+    char *method;
+    char action;
 
     if (argc == 0) {
         return "AllowMethods: No method or 'reset' keyword given";
     }
     if (argc == 1) {
-        if (strcasecmp("reset", argv[0]) == 0) {
-            conf->allowed = 0;
-            conf->allowed_set = 1;
+        if (!ap_cstr_casecmp(argv[0], "reset")) {
+            conf->allowed         = 0;
+            conf->enforce_methods = 0;
+            conf->add             = 0;
+            conf->remove          = 0;
+            conf->allowed_set     = 1;
             return NULL;
         }
     }
 
+    conf->allowed = 0;
+    conf->add     = 0;
+    conf->remove  = 0;
+
     for (i = 0; i < argc; i++) {
         int m;
+        char *w = argv[i];
+
+        if( *w == '-' || *w == '+' ) {
+            if (!merge && !first) {
+                return "Either all methods in AllowMethods must start with + or -, or no methods may.";
+            }
+
+            action = *(w++);
+            method = w;
+            merge = 1;
+        }
+        else if (merge) {
+            return "Either all methods in AllowMethods must start with + or -, or no methods may.";
+        }
+        else {
+            method = w;
+        }
+
+        m = ap_method_number_of((char const*)method);
 
-        m = ap_method_number_of(argv[i]);
         if (m == M_INVALID) {
             return apr_pstrcat(cmd->pool, "AllowMethods: Invalid Method '",
-                               argv[i], "'", NULL);
+                               method, "'", NULL);
+        }
+
+        if (action == '-' ) {
+            conf->remove  |=  (AP_METHOD_BIT << m);
+            conf->add     &= ~(AP_METHOD_BIT << m);
+            conf->allowed &= ~(AP_METHOD_BIT << m);
+        }
+        else if (action == '+' ) {
+            conf->remove  &= ~(AP_METHOD_BIT << m);
+            conf->add     |=  (AP_METHOD_BIT << m);
+            conf->allowed |=  (AP_METHOD_BIT << m);
+        }
+        else {
+            conf->allowed |= (AP_METHOD_BIT << m);
         }
 
-        conf->allowed |= (AP_METHOD_BIT << m);
+        first = 0;
     }
-    conf->allowed_set = 1;
+    conf->allowed_set     = 1;
+    conf->enforce_methods = 1;
+
     return NULL;
 }