]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Backport:
authorGraham Leggett <minfrin@apache.org>
Sat, 21 Aug 2021 22:55:47 +0000 (22:55 +0000)
committerGraham Leggett <minfrin@apache.org>
Sat, 21 Aug 2021 22:55:47 +0000 (22:55 +0000)
  *) mod_dav: Add method_precondition hook. WebDAV extensions define
     conditions that must exist before a WebDAV method can be executed.
     This hook allows a WebDAV extension to verify these preconditions.
     trunk patch: http://svn.apache.org/r1879339
                  http://svn.apache.org/r1879340
     2.4.x patch: https://svn.apache.org/repos/asf/httpd/httpd/patches/2.4.x/httpd-2.4-dav-preconditions1.patch
     +1: minfrin, jim, ylavic (by inspection)

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1892514 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
STATUS
include/ap_mmn.h
modules/dav/main/mod_dav.c
modules/dav/main/mod_dav.h

diff --git a/CHANGES b/CHANGES
index 19cbb1f885c365d64b2c4fe2de669ed031e2b8b5..05aa7a07a58db4915ac1b52b44200fea51af11ef 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,11 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.4.49
 
+  *) mod_dav: Add method_precondition hook. WebDAV extensions define
+     conditions that must exist before a WebDAV method can be executed.
+     This hook allows a WebDAV extension to verify these preconditions.
+     [Graham Leggett]
+
   *) Add hooks deliver_report and gather_reports to mod_dav.h. Allows other
      modules apart from versioning implementations to handle the REPORT method.
      [Graham Leggett]
diff --git a/STATUS b/STATUS
index 5e807e375c812698b8ebcef8c1e2f34c1cea5b92..bc8feff8a554e76ad65e11e596457a3437cf5ce2 100644 (file)
--- a/STATUS
+++ b/STATUS
@@ -142,14 +142,6 @@ RELEASE SHOWSTOPPERS:
 PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
   [ start all new proposals below, under PATCHES PROPOSED. ]
 
-  *) mod_dav: Add method_precondition hook. WebDAV extensions define
-     conditions that must exist before a WebDAV method can be executed.
-     This hook allows a WebDAV extension to verify these preconditions.
-     trunk patch: http://svn.apache.org/r1879339
-                  http://svn.apache.org/r1879340
-     2.4.x patch: https://svn.apache.org/repos/asf/httpd/httpd/patches/2.4.x/httpd-2.4-dav-preconditions1.patch
-     +1: minfrin, jim, ylavic (by inspection)
-     ylavic: requires above httpd-2.4-dav-hooks1.patch first.
 
 PATCHES PROPOSED TO BACKPORT FROM TRUNK:
   [ New proposals should be added at the end of the list ]
index 5899de5b8f804a33173ed76b2059bb38b3381407..a162b8f90deb1efbb555d2b2528d291e5d7344c6 100644 (file)
  *                           dav_close_lockdb() and dav_get_resource() to
  *                           mod_dav.h.
  * 20120211.112 (2.4.49-dev) Add deliver_report and gather_reports hooks.
+ * 20120211.113 (2.4.49-dev) Add method_precondition hook.
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
 #define MODULE_MAGIC_NUMBER_MAJOR 20120211
 #endif
-#define MODULE_MAGIC_NUMBER_MINOR 112                 /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 113                 /* 0...n */
 
 /**
  * Determine if the server's current MODULE_MAGIC_NUMBER is at least a
index 96094697de1d234672dece971d544a0b95fdd3f4..3ba1f8cad7066409e06537411ced74ccbef13ca9 100644 (file)
@@ -860,6 +860,12 @@ static int dav_method_get(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     if (!resource->exists) {
         /* Apache will supply a default error for this. */
         return HTTP_NOT_FOUND;
@@ -907,6 +913,12 @@ static int dav_method_post(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     /* Note: depth == 0. Implies no need for a multistatus response. */
     if ((err = dav_validate_request(r, resource, 0, NULL, NULL,
                                     DAV_VALIDATE_RESOURCE, NULL)) != NULL) {
@@ -940,6 +952,12 @@ static int dav_method_put(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     /* If not a file or collection resource, PUT not allowed */
     if (resource->type != DAV_RESOURCE_TYPE_REGULAR
         && resource->type != DAV_RESOURCE_TYPE_WORKING) {
@@ -1210,6 +1228,13 @@ static int dav_method_delete(request_rec *r)
                            &resource);
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
+
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     if (!resource->exists) {
         /* Apache will supply a default error for this. */
         return HTTP_NOT_FOUND;
@@ -1651,6 +1676,12 @@ static int dav_method_options(request_rec *r)
     }
     /* note: doc == NULL if no request body */
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     if (doc && !dav_validate_root(doc, "options")) {
         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00584)
                       "The \"options\" element was not found.");
@@ -1983,6 +2014,13 @@ static dav_error * dav_propfind_walker(dav_walk_resource *wres, int calltype)
     dav_propdb *propdb;
     dav_get_props_result propstats = { 0 };
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(ctx->r, NULL, wres->resource, ctx->doc, &err) != DECLINED
+            && err) {
+        apr_pool_clear(ctx->scratchpool);
+        return NULL;
+    }
+
     /*
     ** Note: ctx->doc can only be NULL for DAV_PROPFIND_IS_ALLPROP. Since
     ** dav_get_allprops() does not need to do namespace translation,
@@ -2054,6 +2092,17 @@ static int dav_method_propfind(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    if ((result = ap_xml_parse_input(r, &doc)) != OK) {
+        return result;
+    }
+    /* note: doc == NULL if no request body */
+
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     if (dav_get_resource_state(r, resource) == DAV_RESOURCE_NULL) {
         /* Apache will supply a default error for this. */
         return HTTP_NOT_FOUND;
@@ -2081,11 +2130,6 @@ static int dav_method_propfind(request_rec *r)
         }
     }
 
-    if ((result = ap_xml_parse_input(r, &doc)) != OK) {
-        return result;
-    }
-    /* note: doc == NULL if no request body */
-
     if (doc && !dav_validate_root(doc, "propfind")) {
         /* This supplies additional information for the default message. */
         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00585)
@@ -2331,16 +2375,23 @@ static int dav_method_proppatch(request_rec *r)
                            &resource);
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
-    if (!resource->exists) {
-        /* Apache will supply a default error for this. */
-        return HTTP_NOT_FOUND;
-    }
 
     if ((result = ap_xml_parse_input(r, &doc)) != OK) {
         return result;
     }
     /* note: doc == NULL if no request body */
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
+    if (!resource->exists) {
+        /* Apache will supply a default error for this. */
+        return HTTP_NOT_FOUND;
+    }
+
     if (doc == NULL || !dav_validate_root(doc, "propertyupdate")) {
         /* This supplies additional information for the default message. */
         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00587)
@@ -2540,6 +2591,12 @@ static int dav_method_mkcol(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     if (resource->exists) {
         /* oops. something was already there! */
 
@@ -2660,6 +2717,12 @@ static int dav_method_copymove(request_rec *r, int is_move)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     if (!resource->exists) {
         /* Apache will supply a default error for this. */
         return HTTP_NOT_FOUND;
@@ -2726,6 +2789,12 @@ static int dav_method_copymove(request_rec *r, int is_move)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, resnew, NULL, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     /* are the two resources handled by the same repository? */
     if (resource->hooks != resnew->hooks) {
         /* ### this message exposes some backend config, but screw it... */
@@ -3077,6 +3146,12 @@ static int dav_method_lock(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     /* Check if parent collection exists */
     if ((err = resource->hooks->get_parent_resource(resource, &parent)) != NULL) {
         /* ### add a higher-level description? */
@@ -3276,6 +3351,12 @@ static int dav_method_unlock(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     resource_state = dav_get_resource_state(r, resource);
 
     /*
@@ -3335,15 +3416,21 @@ static int dav_method_vsn_control(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
-    /* remember the pre-creation resource state */
-    resource_state = dav_get_resource_state(r, resource);
-
     /* parse the request body (may be a version-control element) */
     if ((result = ap_xml_parse_input(r, &doc)) != OK) {
         return result;
     }
     /* note: doc == NULL if no request body */
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
+    /* remember the pre-creation resource state */
+    resource_state = dav_get_resource_state(r, resource);
+
     if (doc != NULL) {
         const apr_xml_elem *child;
         apr_size_t tsize;
@@ -3588,6 +3675,12 @@ static int dav_method_checkout(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     if (!resource->exists) {
         /* Apache will supply a default error for this. */
         return HTTP_NOT_FOUND;
@@ -3664,6 +3757,12 @@ static int dav_method_uncheckout(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     if (!resource->exists) {
         /* Apache will supply a default error for this. */
         return HTTP_NOT_FOUND;
@@ -3741,6 +3840,12 @@ static int dav_method_checkin(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     if (!resource->exists) {
         /* Apache will supply a default error for this. */
         return HTTP_NOT_FOUND;
@@ -3862,6 +3967,12 @@ static int dav_method_update(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     if (!resource->exists) {
         /* Apache will supply a default error for this. */
         return HTTP_NOT_FOUND;
@@ -3936,6 +4047,9 @@ typedef struct dav_label_walker_ctx
     /* input: */
     dav_walk_params w;
 
+    /* original request */
+    request_rec *r;
+
     /* label being manipulated */
     const char *label;
 
@@ -3955,13 +4069,19 @@ static dav_error * dav_label_walker(dav_walk_resource *wres, int calltype)
     dav_label_walker_ctx *ctx = wres->walk_ctx;
     dav_error *err = NULL;
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(ctx->r, NULL, wres->resource, NULL, &err) != DECLINED
+            && err) {
+       /* precondition failed, dropping through */
+    }
+
     /* Check the state of the resource: must be a version or
      * non-checkedout version selector
      */
     /* ### need a general mechanism for reporting precondition violations
      * ### (should be returning XML document for 403/409 responses)
      */
-    if (wres->resource->type != DAV_RESOURCE_TYPE_VERSION &&
+    else if (wres->resource->type != DAV_RESOURCE_TYPE_VERSION &&
         (wres->resource->type != DAV_RESOURCE_TYPE_REGULAR
          || !wres->resource->versioned)) {
         err = dav_new_error(ctx->w.pool, HTTP_CONFLICT, 0, 0,
@@ -4007,11 +4127,23 @@ static int dav_method_label(request_rec *r)
     if (vsn_hooks == NULL || vsn_hooks->add_label == NULL)
         return DECLINED;
 
+    /* parse the request body */
+    if ((result = ap_xml_parse_input(r, &doc)) != OK) {
+        return result;
+    }
+
     /* Ask repository module to resolve the resource */
     err = dav_get_resource(r, 1 /* label_allowed */, 0 /* use_checked_in */,
                            &resource);
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
+
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     if (!resource->exists) {
         /* Apache will supply a default error for this. */
         return HTTP_NOT_FOUND;
@@ -4023,11 +4155,6 @@ static int dav_method_label(request_rec *r)
         return HTTP_BAD_REQUEST;
     }
 
-    /* parse the request body */
-    if ((result = ap_xml_parse_input(r, &doc)) != OK) {
-        return result;
-    }
-
     if (doc == NULL || !dav_validate_root(doc, "label")) {
         /* This supplies additional information for the default message. */
         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00610)
@@ -4076,6 +4203,7 @@ static int dav_method_label(request_rec *r)
     ctx.w.walk_ctx = &ctx;
     ctx.w.pool = r->pool;
     ctx.w.root = resource;
+    ctx.r = r;
     ctx.vsn_hooks = vsn_hooks;
 
     err = (*resource->hooks->walk)(&ctx.w, depth, &multi_status);
@@ -4187,6 +4315,12 @@ static int dav_method_report(request_rec *r)
         return dav_handle_err(r, err, NULL);
     }
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     if (!resource->exists) {
         /* Apache will supply a default error for this. */
         return HTTP_NOT_FOUND;
@@ -4257,6 +4391,12 @@ static int dav_method_make_workspace(request_rec *r)
         return result;
     }
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     if (doc == NULL
         || !dav_validate_root(doc, "mkworkspace")) {
         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00615)
@@ -4316,6 +4456,12 @@ static int dav_method_make_activity(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     /* MKACTIVITY does not have a defined request body. */
     if ((result = ap_discard_request_body(r)) != OK) {
         return result;
@@ -4441,6 +4587,12 @@ static int dav_method_merge(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, source_resource, NULL, doc, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     no_auto_merge = dav_find_child(doc->root, "no-auto-merge") != NULL;
     no_checkout = dav_find_child(doc->root, "no-checkout") != NULL;
 
@@ -4458,6 +4610,13 @@ static int dav_method_merge(request_rec *r)
                            &resource);
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
+
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, source_resource, resource, doc, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     if (!resource->exists) {
         /* Apache will supply a default error for this. */
         return HTTP_NOT_FOUND;
@@ -4525,6 +4684,12 @@ static int dav_method_bind(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     if (!resource->exists) {
         /* Apache will supply a default error for this. */
         return HTTP_NOT_FOUND;
@@ -4575,6 +4740,12 @@ static int dav_method_bind(request_rec *r)
     if (err != NULL)
         return dav_handle_err(r, err, NULL);
 
+    /* check for any method preconditions */
+    if (dav_run_method_precondition(r, resource, binding, NULL, &err) != DECLINED
+            && err) {
+        return dav_handle_err(r, err, NULL);
+    }
+
     /* are the two resources handled by the same repository? */
     if (resource->hooks != binding->hooks) {
         /* ### this message exposes some backend config, but screw it... */
@@ -4993,6 +5164,7 @@ APR_HOOK_STRUCT(
     APR_HOOK_LINK(insert_all_liveprops)
     APR_HOOK_LINK(deliver_report)
     APR_HOOK_LINK(gather_reports)
+    APR_HOOK_LINK(method_precondition)
     )
 
 APR_IMPLEMENT_EXTERNAL_HOOK_VOID(dav, DAV, gather_propsets,
@@ -5022,3 +5194,9 @@ APR_IMPLEMENT_EXTERNAL_HOOK_VOID(dav, DAV, gather_reports,
                                     apr_array_header_t *reports, dav_error **err),
                                    (r, resource, reports, err))
 
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(dav, DAV, int, method_precondition,
+                                      (request_rec *r,
+                                       dav_resource *src, const dav_resource *dest,
+                                       const apr_xml_doc *doc,
+                                       dav_error **err),
+                                       (r, src, dest, doc, err), DECLINED)
index 5b4c36bea70bf9193f76b8eaadb6ceee93cfe3b5..9bd8980ee1dc2bc88a4e34ae461b6edbdc644dcb 100644 (file)
@@ -741,6 +741,40 @@ APR_DECLARE_EXTERNAL_HOOK(dav, DAV, void, gather_reports,
                           (request_rec *r, const dav_resource *resource,
                            apr_array_header_t *reports, dav_error **err))
 
+/*
+ ** method_precondition: check method preconditions.
+ **
+ ** If a WebDAV extension needs to set any preconditions on a method, this
+ ** hook is where to do it. If the precondition fails, return an error
+ ** response with the tagname set to the value of the failed precondition.
+ **
+ ** If the method requires an XML body, this will be read and provided as
+ ** the doc value. If not, doc is NULL. An extension that needs to verify
+ ** the non-XML body of a request should register an input filter to do so
+ ** within this hook.
+ **
+ ** Methods like PUT will supply a single src resource, and the dst will
+ ** be NULL.
+ **
+ ** Methods like COPY or MOVE will trigger this hook twice. The first
+ ** invocation will supply just the source resource. The second invocation
+ ** will supply a source and destination. This allows preconditions on the
+ ** source resource to be verified before making an attempt to get the
+ ** destination resource.
+ **
+ ** Methods like PROPFIND and LABEL will trigger this hook initially for
+ ** the src resource, and then subsequently for each resource that has
+ ** been walked during processing, with the walked resource passed in dst,
+ ** and NULL passed in src.
+ **
+ ** As a rule, the src resource originates from a request that has passed
+ ** through httpd's authn/authz hooks, while the dst resource has not.
+ */
+APR_DECLARE_EXTERNAL_HOOK(dav, DAV, int, method_precondition,
+                          (request_rec *r,
+                           dav_resource *src, const dav_resource *dst,
+                           const apr_xml_doc *doc, dav_error **err))
+
 
 DAV_DECLARE(const dav_hooks_locks *) dav_get_lock_hooks(request_rec *r);
 DAV_DECLARE(const dav_hooks_propdb *) dav_get_propdb_hooks(request_rec *r);