Browse Source

[mod_webdav] limit mem use under extreme condition

limit memory use under extreme conditions (edge cases)
master
Glenn Strauss 8 months ago
parent
commit
7283c43566
  1. 139
      src/mod_webdav.c

139
src/mod_webdav.c

@ -230,6 +230,7 @@
#include "buffer.h"
#include "chunk.h"
#include "fdevent.h"
#include "http_chunk.h"
#include "http_date.h"
#include "http_etag.h"
#include "http_header.h"
@ -621,6 +622,19 @@ URIHANDLER_FUNC(mod_webdav_uri_handler)
}
static void
webdav_double_buffer (request_st * const r, buffer * const b)
{
/* send parts of XML to r->write_queue; surrounding XML tags added later.
* http_chunk_append_buffer() is safe to use here since r->resp_body_started
* has not been set, so r->resp_send_chunked can not be set yet */
if (buffer_string_length(b) > 60000) {
http_chunk_append_buffer(r, b); /*(might move/steal/reset buffer)*/
buffer_clear(b);
}
}
#ifdef USE_LOCKS
typedef struct webdav_lockdata_wr {
@ -858,16 +872,20 @@ webdav_xml_propstat (buffer * const b, buffer * const value, const int status)
__attribute_cold__
static void
webdav_xml_response_status (buffer * const b,
webdav_xml_response_status (request_st * const r,
const buffer * const href,
const int status)
{
buffer * const b = chunk_buffer_acquire();
buffer_append_string_len(b, CONST_STR_LEN(
"<D:response>\n"));
webdav_xml_href(b, href);
webdav_xml_status(b, status);
buffer_append_string_len(b, CONST_STR_LEN(
"</D:response>\n"));
/*(under extreme error conditions, write() to tempfile for each error)*/
http_chunk_append_buffer(r, b); /*(might move/steal/reset buffer)*/
chunk_buffer_release(b);
}
@ -940,8 +958,7 @@ webdav_xml_activelock (buffer * const b,
static void
webdav_xml_doc_multistatus (request_st * const r,
const plugin_config * const pconf,
buffer * const ms)
const plugin_config * const pconf)
{
http_status_set_fin(r, 207); /* Multi-status */
@ -951,7 +968,7 @@ webdav_xml_doc_multistatus (request_st * const r,
buffer_append_string_len(b, CONST_STR_LEN(
"<D:multistatus xmlns:D=\"DAV:\">\n"));
chunkqueue_prepend_buffer_commit(cq);
chunkqueue_append_buffer(cq, ms); /*(might move/steal/reset buffer)*/
chunkqueue_append_mem(cq, CONST_STR_LEN(
"</D:multistatus>\n"));
@ -1083,28 +1100,25 @@ webdav_xml_doc_423_locked (request_st * const r, buffer * const hrefs,
http_status_set(r, 423); /* Locked */
r->resp_body_finished = 1;
buffer * const b = /*(optimization; buf extended as needed)*/
chunkqueue_append_buffer_open_sz(&r->write_queue, 256 + hrefs->used);
chunkqueue * const cq = &r->write_queue;
buffer * const b = chunkqueue_prepend_buffer_open(cq);
webdav_xml_doctype(b, r);
struct const_iovec iov[] = {
{ CONST_STR_LEN(
buffer_append_str3(b,
CONST_STR_LEN(
"<D:error xmlns:D=\"DAV:\">\n"
"<D:") }
,{ errtag, errtaglen }
,{ CONST_STR_LEN(
">\n") }
,{ CONST_BUF_LEN(hrefs) }
,{ CONST_STR_LEN(
"</D:") }
,{ errtag, errtaglen }
,{ CONST_STR_LEN(
"<D:"),
errtag, errtaglen,
CONST_STR_LEN(
">\n"));
chunkqueue_prepend_buffer_commit(cq);
buffer_append_str3(hrefs,
CONST_STR_LEN(
"</D:"),
errtag, errtaglen,
CONST_STR_LEN(
">\n"
"</D:error>\n") }
};
buffer_append_iovec(b, iov, sizeof(iov)/sizeof(*iov));
chunkqueue_append_buffer_commit(&r->write_queue);
"</D:error>\n"));
chunkqueue_append_buffer(cq, hrefs); /*(might move/steal/reset buffer)*/
}
#endif
@ -2367,7 +2381,7 @@ webdav_delete_file (const plugin_config * const pconf,
static int
webdav_delete_dir (const plugin_config * const pconf,
physical_st * const dst,
buffer * const b,
request_st * const r,
const int flags)
{
int multi_status = 0;
@ -2380,7 +2394,7 @@ webdav_delete_dir (const plugin_config * const pconf,
#endif
if (NULL == dir) {
if (dfd >= 0) close(dfd);
webdav_xml_response_status(b, &dst->rel_path, 403);
webdav_xml_response_status(r, &dst->rel_path, 403);
return 1;
}
@ -2436,13 +2450,13 @@ webdav_delete_dir (const plugin_config * const pconf,
if (s_isdir) {
buffer_append_string_len(&dst->path, CONST_STR_LEN("/"));
buffer_append_string_len(&dst->rel_path, CONST_STR_LEN("/"));
multi_status |= webdav_delete_dir(pconf, dst, b, flags);
multi_status |= webdav_delete_dir(pconf, dst, r, flags);
}
else {
int status =
webdav_unlinkat(pconf, dst, dfd, de->d_name);
if (0 != status) {
webdav_xml_response_status(b, &dst->rel_path, status);
webdav_xml_response_status(r, &dst->rel_path, status);
multi_status = 1;
}
}
@ -2465,7 +2479,7 @@ webdav_delete_dir (const plugin_config * const pconf,
}
}
if (0 != rmdir_status) {
webdav_xml_response_status(b, &dst->rel_path, rmdir_status);
webdav_xml_response_status(r, &dst->rel_path, rmdir_status);
multi_status = 1;
}
}
@ -2739,7 +2753,7 @@ static int
webdav_copymove_dir (const plugin_config * const pconf,
physical_st * const src,
physical_st * const dst,
buffer * const b,
request_st * const r,
int flags)
{
/* NOTE: merging collections is NON-CONFORMANT behavior
@ -2795,7 +2809,7 @@ webdav_copymove_dir (const plugin_config * const pconf,
#ifndef RENAME_NOREPLACE /*(renameat2() not well-supported yet)*/
if (!overwrite) {
if (0 == lstat(dst->path.ptr, &st) || errno != ENOENT) {
webdav_xml_response_status(b, &src->rel_path, 412);
webdav_xml_response_status(r, &src->rel_path, 412);
return 412; /* Precondition Failed */
}
/* TOC-TOU race between lstat() and rename(),
@ -2818,14 +2832,14 @@ webdav_copymove_dir (const plugin_config * const pconf,
case ENOTEMPTY:
#endif
if (!overwrite) {
webdav_xml_response_status(b, &src->rel_path, 412);
webdav_xml_response_status(r, &src->rel_path, 412);
return 412; /* Precondition Failed */
}
make_destdir = 0;
break;
case ENOTDIR:
if (!overwrite) {
webdav_xml_response_status(b, &src->rel_path, 409);
webdav_xml_response_status(r, &src->rel_path, 409);
return 409; /* Conflict */
}
@ -2852,7 +2866,7 @@ webdav_copymove_dir (const plugin_config * const pconf,
dst->path.ptr[dst->path.used-2] = '/'; /*(restore slash)*/
dst->rel_path.ptr[dst->rel_path.used-2] = '/';
if (0 != status) {
webdav_xml_response_status(b, &src->rel_path, status);
webdav_xml_response_status(r, &src->rel_path, status);
return status;
}
@ -2883,7 +2897,7 @@ webdav_copymove_dir (const plugin_config * const pconf,
if (make_destdir) {
if (0 != (status = webdav_mkdir(pconf, dst, overwrite))) {
webdav_xml_response_status(b, &src->rel_path, status);
webdav_xml_response_status(r, &src->rel_path, status);
return status;
}
}
@ -2908,7 +2922,7 @@ webdav_copymove_dir (const plugin_config * const pconf,
#endif
if (NULL == srcdir) {
if (dfd >= 0) close(dfd);
webdav_xml_response_status(b, &src->rel_path, 403);
webdav_xml_response_status(r, &src->rel_path, 403);
return 403; /* Forbidden */
}
mode_t d_type;
@ -2963,14 +2977,14 @@ webdav_copymove_dir (const plugin_config * const pconf,
buffer_append_string_len(&dst->path, CONST_STR_LEN("/"));
buffer_append_string_len(&src->rel_path, CONST_STR_LEN("/"));
buffer_append_string_len(&dst->rel_path, CONST_STR_LEN("/"));
status = webdav_copymove_dir(pconf, src, dst, b, flags);
status = webdav_copymove_dir(pconf, src, dst, r, flags);
if (0 != status)
multi_status = 1;
}
else if (S_ISREG(d_type)) {
status = webdav_copymove_file(pconf, src, dst, &flags);
if (0 != status)
webdav_xml_response_status(b, &src->rel_path, status);
webdav_xml_response_status(r, &src->rel_path, status);
}
#if 0
else if (S_ISLNK(d_type)) {
@ -2998,9 +3012,9 @@ webdav_copymove_dir (const plugin_config * const pconf,
if (0 == multi_status) {
if (flags & (WEBDAV_FLAG_MOVE_RENAME|WEBDAV_FLAG_MOVE_XDEV)) {
status = webdav_delete_dir(pconf, src, b, flags); /* content */
status = webdav_delete_dir(pconf, src, r, flags); /* content */
if (0 != status) {
webdav_xml_response_status(b, &src->rel_path, status);
webdav_xml_response_status(r, &src->rel_path, status);
multi_status = 1;
}
}
@ -3383,6 +3397,8 @@ webdav_propfind_resource_403 (const webdav_propfind_bufs * const restrict pb)
buffer_append_string_len(b, CONST_STR_LEN(
"</D:propstat>\n"
"</D:response>\n"));
webdav_double_buffer(pb->r, b);
}
@ -3423,6 +3439,8 @@ webdav_propfind_resource (const webdav_propfind_bufs * const restrict pb)
webdav_xml_propstat(b, b_404, 404);
buffer_append_string_len(b, CONST_STR_LEN(
"</D:response>\n"));
webdav_double_buffer(pb->r, b);
}
@ -3671,6 +3689,7 @@ struct webdav_lock_token_submitted_st {
int size;
const buffer *authn_user;
buffer *b;
request_st *r;
int nlocks;
int slocks;
int smatch;
@ -3722,8 +3741,10 @@ webdav_lock_token_submitted_cb (void * const vdata,
}
/* no match with lock tokens in request */
if (!shared)
if (!shared) {
webdav_xml_href(cbdata->b, &lockdata->lockroot);
webdav_double_buffer(cbdata->r, cbdata->b);
}
}
@ -3745,6 +3766,7 @@ webdav_has_lock (request_st * const r,
struct webdav_lock_token_submitted_st cbdata;
cbdata.b = chunk_buffer_acquire();
cbdata.r = r;
cbdata.tokens = NULL;
cbdata.used = 0;
cbdata.size = 0;
@ -3874,7 +3896,7 @@ webdav_has_lock (request_st * const r,
int has_lock = 1;
if (0 != cbdata.b->used)
if (0 != cbdata.b->used || !chunkqueue_is_empty(&r->write_queue))
has_lock = 0;
else if (0 == cbdata.nlocks) { /* resource is not locked at all */
/* error if lock provided on source and no locks present on source;
@ -4118,10 +4140,11 @@ mod_webdav_propfind (request_st * const r, const plugin_config * const pconf)
pb.r = r;
pb.pconf = pconf;
pb.dst = &r->physical;
pb.b = /*(optimization; buf extended as needed)*/
chunkqueue_append_buffer_open_sz(&r->write_queue, 8192);
pb.b = chunk_buffer_acquire();
pb.b_200 = chunk_buffer_acquire();
pb.b_404 = chunk_buffer_acquire();
/*(optimization; buf extended as needed)*/
chunk_buffer_prepare_append(pb.b, 8192);
webdav_xml_doctype(pb.b, r);
buffer_append_string_len(pb.b, CONST_STR_LEN(
@ -4135,7 +4158,8 @@ mod_webdav_propfind (request_st * const r, const plugin_config * const pconf)
buffer_append_string_len(pb.b, CONST_STR_LEN(
"</D:multistatus>\n"));
chunkqueue_append_buffer_commit(&r->write_queue);
http_chunk_append_buffer(r, pb.b); /*(might move/steal/reset buffer)*/
chunk_buffer_release(pb.b);
http_status_set_fin(r, 207); /* Multi-status */
chunk_buffer_release(pb.b_404);
@ -4220,12 +4244,10 @@ mod_webdav_delete (request_st * const r, const plugin_config * const pconf)
return HANDLER_FINISHED;
}
buffer * const ms = chunk_buffer_acquire(); /* multi-status */
const int flags = (r->conf.force_lowercase_filenames)
? WEBDAV_FLAG_LC_NAMES
: 0;
if (0 == webdav_delete_dir(pconf, &r->physical, ms, flags)) {
if (0 == webdav_delete_dir(pconf, &r->physical, r, flags)) {
/* Note: this does not destroy locks if an error occurs,
* which is not a problem if lock is only on the collection
* being moved, but might need finer updates if there are
@ -4234,10 +4256,9 @@ mod_webdav_delete (request_st * const r, const plugin_config * const pconf)
http_status_set_fin(r, 204); /* No Content */
}
else {
webdav_xml_doc_multistatus(r, pconf, ms); /* 207 Multi-status */
webdav_xml_doc_multistatus(r, pconf); /* 207 Multi-status */
}
chunk_buffer_release(ms);
/* invalidate stat cache of src if DELETE, whether or not successful */
stat_cache_delete_dir(CONST_BUF_LEN(&r->physical.path));
}
@ -4962,8 +4983,7 @@ mod_webdav_copymove_b (request_st * const r, const plugin_config * const pconf,
return HANDLER_FINISHED;
}
buffer * const ms = chunk_buffer_acquire(); /* multi-status */
if (0 == webdav_copymove_dir(pconf, &r->physical, dst, ms, flags)) {
if (0 == webdav_copymove_dir(pconf, &r->physical, dst, r, flags)) {
if (r->http_method == HTTP_METHOD_MOVE)
webdav_lock_delete_uri_col(pconf, &r->physical.rel_path);
/*(requiring lock on destination requires MKCOL create dst first)
@ -4976,9 +4996,8 @@ mod_webdav_copymove_b (request_st * const r, const plugin_config * const pconf,
* which is not a problem if lock is only on the collection
* being moved, but might need finer updates if there are
* locks on internal elements that are successfully moved */
webdav_xml_doc_multistatus(r, pconf, ms); /* 207 Multi-status */
webdav_xml_doc_multistatus(r, pconf); /* 207 Multi-status */
}
chunk_buffer_release(ms);
/* invalidate stat cache of src if MOVE, whether or not successful */
if (r->http_method == HTTP_METHOD_MOVE)
stat_cache_delete_dir(CONST_BUF_LEN(&r->physical.path));
@ -5212,6 +5231,7 @@ mod_webdav_proppatch (request_st * const r, const plugin_config * const pconf)
"no namespace for: %s", prop->name);
if (!ms) ms = chunk_buffer_acquire(); /* Unprocessable Entity */
webdav_xml_propstat_status(ms, "", (char *)prop->name, 422);
webdav_double_buffer(r, ms);
continue;
}
@ -5230,6 +5250,7 @@ mod_webdav_proppatch (request_st * const r, const plugin_config * const pconf)
if (!ms) ms = chunk_buffer_acquire();
webdav_xml_propstat_protected(ms, (char *)prop->name,
namelen, 403); /* Forbidden */
webdav_double_buffer(r, ms);
continue;
}
}
@ -5264,8 +5285,8 @@ mod_webdav_proppatch (request_st * const r, const plugin_config * const pconf)
/* workaround Microsoft-WebDAV-MiniRedir bug; 204 not handled */
/* 200 without response body or 204 both incorrectly interpreted
* as 507 Insufficient Storage by Microsoft-WebDAV-MiniRedir. */
ms = chunk_buffer_acquire(); /* 207 Multi-status */
webdav_xml_response_status(ms, &r->physical.path, 200);
ms = chunk_buffer_acquire(); /* 207 Multi-status */ /*(flag)*/
webdav_xml_response_status(r, &r->physical.path, 200);
}
}
if (NULL == ms)
@ -5289,6 +5310,7 @@ mod_webdav_proppatch (request_st * const r, const plugin_config * const pconf)
struct webdav_conflicting_lock_st {
webdav_lockdata *lockdata;
buffer *b;
request_st *r;
};
@ -5301,8 +5323,10 @@ webdav_conflicting_lock_cb (void * const vdata,
struct webdav_conflicting_lock_st * const cbdata =
(struct webdav_conflicting_lock_st *)vdata;
if (lockdata->lockscope->used == sizeof("exclusive")
|| cbdata->lockdata->lockscope->used == sizeof("exclusive"))
|| cbdata->lockdata->lockscope->used == sizeof("exclusive")) {
webdav_xml_href(cbdata->b, &lockdata->lockroot);
webdav_double_buffer(cbdata->r, cbdata->b);
}
}
@ -5471,10 +5495,11 @@ mod_webdav_lock (request_st * const r, const plugin_config * const pconf)
struct webdav_conflicting_lock_st cbdata;
cbdata.lockdata = &lockdata;
cbdata.b = chunk_buffer_acquire();
cbdata.r = r;
webdav_lock_activelocks(pconf, &lockdata.lockroot,
(0 == lockdata.depth ? 1 : -1),
webdav_conflicting_lock_cb, &cbdata);
if (0 != cbdata.b->used) {
if (0 != cbdata.b->used || !chunkqueue_is_empty(&r->write_queue)) {
/* 423 Locked */
webdav_xml_doc_error_no_conflicting_lock(r, cbdata.b);
chunk_buffer_release(cbdata.b);

Loading…
Cancel
Save