From: Graham Leggett Date: Sat, 21 Aug 2021 22:55:47 +0000 (+0000) Subject: Backport: X-Git-Tag: candidate-2.4.49~3^2~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c7c1f3905cfb9b307fef1671eeeb144b3aecc345;p=thirdparty%2Fapache%2Fhttpd.git Backport: *) 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 --- diff --git a/CHANGES b/CHANGES index 19cbb1f885c..05aa7a07a58 100644 --- 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 5e807e375c8..bc8feff8a55 100644 --- 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 ] diff --git a/include/ap_mmn.h b/include/ap_mmn.h index 5899de5b8f8..a162b8f90de 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -573,6 +573,7 @@ * 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" */ @@ -580,7 +581,7 @@ #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 diff --git a/modules/dav/main/mod_dav.c b/modules/dav/main/mod_dav.c index 96094697de1..3ba1f8cad70 100644 --- a/modules/dav/main/mod_dav.c +++ b/modules/dav/main/mod_dav.c @@ -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) diff --git a/modules/dav/main/mod_dav.h b/modules/dav/main/mod_dav.h index 5b4c36bea70..9bd8980ee1d 100644 --- a/modules/dav/main/mod_dav.h +++ b/modules/dav/main/mod_dav.h @@ -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);